mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-11-06 09:29:31 +00:00
* Formally deprecate CodeNotary build config * Remove CodeNotary specific integrity checking The current code is specific to how CodeNotary was doing integrity checking. A future integrity checking mechanism likely will work differently (e.g. through EROFS based containers). Remove the current code to make way for a future implementation. * Drop CodeNotary integrity fixups * Drop unused tests * Fix pytest * Fix pytest * Remove CodeNotary related exceptions and handling Remove CodeNotary related exceptions and handling from the Docker interface. * Drop unnecessary comment * Remove Codenotary specific IssueType/SuggestionType * Drop Codenotary specific environment and secret reference * Remove unused constants * Introduce APIGone exception for removed APIs Introduce a new exception class APIGone to indicate that certain API features have been removed and are no longer available. Update the security integrity check endpoint to raise this new exception instead of a generic APIError, providing clearer communication to clients that the feature has been intentionally removed. * Drop content trust A cosign based signature verification will likely be named differently to avoid confusion with existing implementations. For now, remove the content trust option entirely. * Drop code sign test * Remove source_mods/content_trust evaluations * Remove content_trust reference in bootstrap.py * Fix security tests * Drop unused tests * Drop codenotary from schema Since we have "remove extra" in voluptuous, we can remove the codenotary field from the addon schema. * Remove content_trust from tests * Remove content_trust unsupported reason * Remove unnecessary comment * Remove unrelated pytest * Remove unrelated fixtures
548 lines
18 KiB
Python
548 lines
18 KiB
Python
"""Validate add-ons options schema."""
|
|
|
|
import logging
|
|
import re
|
|
import secrets
|
|
from typing import Any
|
|
import uuid
|
|
|
|
import voluptuous as vol
|
|
|
|
from ..const import (
|
|
ARCH_ALL,
|
|
ATTR_ACCESS_TOKEN,
|
|
ATTR_ADVANCED,
|
|
ATTR_APPARMOR,
|
|
ATTR_ARCH,
|
|
ATTR_ARGS,
|
|
ATTR_AUDIO,
|
|
ATTR_AUDIO_INPUT,
|
|
ATTR_AUDIO_OUTPUT,
|
|
ATTR_AUTH_API,
|
|
ATTR_AUTO_UPDATE,
|
|
ATTR_BACKUP_EXCLUDE,
|
|
ATTR_BACKUP_POST,
|
|
ATTR_BACKUP_PRE,
|
|
ATTR_BOOT,
|
|
ATTR_BUILD_FROM,
|
|
ATTR_CONFIGURATION,
|
|
ATTR_DESCRIPTON,
|
|
ATTR_DEVICES,
|
|
ATTR_DEVICETREE,
|
|
ATTR_DISCOVERY,
|
|
ATTR_DOCKER_API,
|
|
ATTR_ENVIRONMENT,
|
|
ATTR_FIELDS,
|
|
ATTR_FULL_ACCESS,
|
|
ATTR_GPIO,
|
|
ATTR_HASSIO_API,
|
|
ATTR_HASSIO_ROLE,
|
|
ATTR_HOMEASSISTANT,
|
|
ATTR_HOMEASSISTANT_API,
|
|
ATTR_HOST_DBUS,
|
|
ATTR_HOST_IPC,
|
|
ATTR_HOST_NETWORK,
|
|
ATTR_HOST_PID,
|
|
ATTR_HOST_UTS,
|
|
ATTR_IMAGE,
|
|
ATTR_INGRESS,
|
|
ATTR_INGRESS_ENTRY,
|
|
ATTR_INGRESS_PANEL,
|
|
ATTR_INGRESS_PORT,
|
|
ATTR_INGRESS_STREAM,
|
|
ATTR_INGRESS_TOKEN,
|
|
ATTR_INIT,
|
|
ATTR_JOURNALD,
|
|
ATTR_KERNEL_MODULES,
|
|
ATTR_LABELS,
|
|
ATTR_LEGACY,
|
|
ATTR_LOCATION,
|
|
ATTR_MACHINE,
|
|
ATTR_MAP,
|
|
ATTR_NAME,
|
|
ATTR_NETWORK,
|
|
ATTR_OPTIONS,
|
|
ATTR_PANEL_ADMIN,
|
|
ATTR_PANEL_ICON,
|
|
ATTR_PANEL_TITLE,
|
|
ATTR_PORTS,
|
|
ATTR_PORTS_DESCRIPTION,
|
|
ATTR_PRIVILEGED,
|
|
ATTR_PROTECTED,
|
|
ATTR_REALTIME,
|
|
ATTR_REPOSITORY,
|
|
ATTR_SCHEMA,
|
|
ATTR_SERVICES,
|
|
ATTR_SLUG,
|
|
ATTR_SQUASH,
|
|
ATTR_STAGE,
|
|
ATTR_STARTUP,
|
|
ATTR_STATE,
|
|
ATTR_STDIN,
|
|
ATTR_SYSTEM,
|
|
ATTR_SYSTEM_MANAGED,
|
|
ATTR_SYSTEM_MANAGED_CONFIG_ENTRY,
|
|
ATTR_TIMEOUT,
|
|
ATTR_TMPFS,
|
|
ATTR_TRANSLATIONS,
|
|
ATTR_TYPE,
|
|
ATTR_UART,
|
|
ATTR_UDEV,
|
|
ATTR_ULIMITS,
|
|
ATTR_URL,
|
|
ATTR_USB,
|
|
ATTR_USER,
|
|
ATTR_UUID,
|
|
ATTR_VERSION,
|
|
ATTR_VIDEO,
|
|
ATTR_WATCHDOG,
|
|
ATTR_WEBUI,
|
|
ROLE_ALL,
|
|
ROLE_DEFAULT,
|
|
AddonBoot,
|
|
AddonBootConfig,
|
|
AddonStage,
|
|
AddonStartup,
|
|
AddonState,
|
|
)
|
|
from ..docker.const import Capabilities
|
|
from ..validate import (
|
|
docker_image,
|
|
docker_ports,
|
|
docker_ports_description,
|
|
network_port,
|
|
token,
|
|
uuid_match,
|
|
version_tag,
|
|
)
|
|
from .const import (
|
|
ATTR_BACKUP,
|
|
ATTR_BREAKING_VERSIONS,
|
|
ATTR_CODENOTARY,
|
|
ATTR_PATH,
|
|
ATTR_READ_ONLY,
|
|
RE_SLUG,
|
|
AddonBackupMode,
|
|
MappingType,
|
|
)
|
|
from .options import RE_SCHEMA_ELEMENT
|
|
|
|
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
|
|
|
RE_VOLUME = re.compile(
|
|
r"^(data|config|ssl|addons|backup|share|media|homeassistant_config|all_addon_configs|addon_config)(?::(rw|ro))?$"
|
|
)
|
|
RE_SERVICE = re.compile(r"^(?P<service>mqtt|mysql):(?P<rights>provide|want|need)$")
|
|
|
|
|
|
RE_DOCKER_IMAGE_BUILD = re.compile(
|
|
r"^([a-zA-Z\-\.:\d{}]+/)*?([\-\w{}]+)/([\-\w{}]+)(:[\.\-\w{}]+)?$"
|
|
)
|
|
|
|
SCHEMA_ELEMENT = vol.Schema(
|
|
vol.Any(
|
|
vol.Match(RE_SCHEMA_ELEMENT),
|
|
[
|
|
# A list may not directly contain another list
|
|
vol.Any(
|
|
vol.Match(RE_SCHEMA_ELEMENT),
|
|
{str: vol.Self},
|
|
)
|
|
],
|
|
{str: vol.Self},
|
|
)
|
|
)
|
|
|
|
RE_MACHINE = re.compile(
|
|
r"^!?(?:"
|
|
r"|intel-nuc"
|
|
r"|generic-x86-64"
|
|
r"|odroid-c2"
|
|
r"|odroid-c4"
|
|
r"|odroid-m1"
|
|
r"|odroid-n2"
|
|
r"|odroid-xu"
|
|
r"|qemuarm-64"
|
|
r"|qemuarm"
|
|
r"|qemux86-64"
|
|
r"|qemux86"
|
|
r"|raspberrypi"
|
|
r"|raspberrypi2"
|
|
r"|raspberrypi3-64"
|
|
r"|raspberrypi3"
|
|
r"|raspberrypi4-64"
|
|
r"|raspberrypi4"
|
|
r"|raspberrypi5-64"
|
|
r"|yellow"
|
|
r"|green"
|
|
r"|tinker"
|
|
r")$"
|
|
)
|
|
|
|
RE_SLUG_FIELD = re.compile(r"^" + RE_SLUG + r"$")
|
|
|
|
|
|
def _warn_addon_config(config: dict[str, Any]):
|
|
"""Warn about miss configs."""
|
|
name = config.get(ATTR_NAME)
|
|
if not name:
|
|
raise vol.Invalid("Invalid Add-on config!")
|
|
|
|
if config.get(ATTR_FULL_ACCESS, False) and (
|
|
config.get(ATTR_DEVICES)
|
|
or config.get(ATTR_UART)
|
|
or config.get(ATTR_USB)
|
|
or config.get(ATTR_GPIO)
|
|
):
|
|
_LOGGER.warning(
|
|
"Add-on have full device access, and selective device access in the configuration. Please report this to the maintainer of %s",
|
|
name,
|
|
)
|
|
|
|
if config.get(ATTR_BACKUP, AddonBackupMode.HOT) == AddonBackupMode.COLD and (
|
|
config.get(ATTR_BACKUP_POST) or config.get(ATTR_BACKUP_PRE)
|
|
):
|
|
_LOGGER.warning(
|
|
"Add-on which only support COLD backups trying to use post/pre commands. Please report this to the maintainer of %s",
|
|
name,
|
|
)
|
|
|
|
if ATTR_CODENOTARY in config:
|
|
_LOGGER.warning(
|
|
"Add-on '%s' uses deprecated 'codenotary' field in config. This field is no longer used and will be ignored. Please report this to the maintainer.",
|
|
name,
|
|
)
|
|
|
|
return config
|
|
|
|
|
|
def _migrate_addon_config(protocol=False):
|
|
"""Migrate addon config."""
|
|
|
|
def _migrate(config: dict[str, Any]):
|
|
name = config.get(ATTR_NAME)
|
|
if not name:
|
|
raise vol.Invalid("Invalid Add-on config!")
|
|
|
|
# Startup 2018-03-30
|
|
if config.get(ATTR_STARTUP) in ("before", "after"):
|
|
value = config[ATTR_STARTUP]
|
|
if protocol:
|
|
_LOGGER.warning(
|
|
"Add-on config 'startup' with '%s' is deprecated. Please report this to the maintainer of %s",
|
|
value,
|
|
name,
|
|
)
|
|
if value == "before":
|
|
config[ATTR_STARTUP] = AddonStartup.SERVICES
|
|
elif value == "after":
|
|
config[ATTR_STARTUP] = AddonStartup.APPLICATION
|
|
|
|
# UART 2021-01-20
|
|
if "auto_uart" in config:
|
|
if protocol:
|
|
_LOGGER.warning(
|
|
"Add-on config 'auto_uart' is deprecated, use 'uart'. Please report this to the maintainer of %s",
|
|
name,
|
|
)
|
|
config[ATTR_UART] = config.pop("auto_uart")
|
|
|
|
# Device 2021-01-20
|
|
if ATTR_DEVICES in config and any(":" in line for line in config[ATTR_DEVICES]):
|
|
if protocol:
|
|
_LOGGER.warning(
|
|
"Add-on config 'devices' use a deprecated format, the new format uses a list of paths only. Please report this to the maintainer of %s",
|
|
name,
|
|
)
|
|
config[ATTR_DEVICES] = [line.split(":")[0] for line in config[ATTR_DEVICES]]
|
|
|
|
# TMPFS 2021-02-01
|
|
if ATTR_TMPFS in config and not isinstance(config[ATTR_TMPFS], bool):
|
|
if protocol:
|
|
_LOGGER.warning(
|
|
"Add-on config 'tmpfs' use a deprecated format, new it's only a boolean. Please report this to the maintainer of %s",
|
|
name,
|
|
)
|
|
config[ATTR_TMPFS] = True
|
|
|
|
# 2021-06 "snapshot" renamed to "backup"
|
|
for entry in (
|
|
"snapshot_exclude",
|
|
"snapshot_post",
|
|
"snapshot_pre",
|
|
"snapshot",
|
|
):
|
|
if entry in config:
|
|
new_entry = entry.replace("snapshot", "backup")
|
|
config[new_entry] = config.pop(entry)
|
|
_LOGGER.warning(
|
|
"Add-on config '%s' is deprecated, '%s' should be used instead. Please report this to the maintainer of %s",
|
|
entry,
|
|
new_entry,
|
|
name,
|
|
)
|
|
|
|
# 2023-11 "map" entries can also be dict to allow path configuration
|
|
volumes = []
|
|
for entry in config.get(ATTR_MAP, []):
|
|
if isinstance(entry, dict):
|
|
# Validate that dict entries have required 'type' field
|
|
if ATTR_TYPE not in entry:
|
|
_LOGGER.warning(
|
|
"Add-on config has invalid map entry missing 'type' field: %s. Skipping invalid entry for %s",
|
|
entry,
|
|
name,
|
|
)
|
|
continue
|
|
volumes.append(entry)
|
|
if isinstance(entry, str):
|
|
result = RE_VOLUME.match(entry)
|
|
if not result:
|
|
_LOGGER.warning(
|
|
"Add-on config has invalid map entry: %s. Skipping invalid entry for %s",
|
|
entry,
|
|
name,
|
|
)
|
|
continue
|
|
volumes.append(
|
|
{
|
|
ATTR_TYPE: result.group(1),
|
|
ATTR_READ_ONLY: result.group(2) != "rw",
|
|
}
|
|
)
|
|
|
|
# Always update config to clear potentially malformed ones
|
|
config[ATTR_MAP] = volumes
|
|
|
|
# 2023-10 "config" became "homeassistant" so /config can be used for addon's public config
|
|
if any(volume[ATTR_TYPE] == MappingType.CONFIG for volume in volumes):
|
|
if any(
|
|
volume
|
|
and volume[ATTR_TYPE]
|
|
in {MappingType.ADDON_CONFIG, MappingType.HOMEASSISTANT_CONFIG}
|
|
for volume in volumes
|
|
):
|
|
_LOGGER.warning(
|
|
"Add-on config using incompatible map options, '%s' and '%s' are ignored if '%s' is included. Please report this to the maintainer of %s",
|
|
MappingType.ADDON_CONFIG,
|
|
MappingType.HOMEASSISTANT_CONFIG,
|
|
MappingType.CONFIG,
|
|
name,
|
|
)
|
|
else:
|
|
_LOGGER.debug(
|
|
"Add-on config using deprecated map option '%s' instead of '%s'. Please report this to the maintainer of %s",
|
|
MappingType.CONFIG,
|
|
MappingType.HOMEASSISTANT_CONFIG,
|
|
name,
|
|
)
|
|
|
|
return config
|
|
|
|
return _migrate
|
|
|
|
|
|
# pylint: disable=no-value-for-parameter
|
|
_SCHEMA_ADDON_CONFIG = vol.Schema(
|
|
{
|
|
vol.Required(ATTR_NAME): str,
|
|
vol.Required(ATTR_VERSION): version_tag,
|
|
vol.Required(ATTR_SLUG): vol.Match(RE_SLUG_FIELD),
|
|
vol.Required(ATTR_DESCRIPTON): str,
|
|
vol.Required(ATTR_ARCH): [vol.In(ARCH_ALL)],
|
|
vol.Optional(ATTR_MACHINE): vol.All([vol.Match(RE_MACHINE)], vol.Unique()),
|
|
vol.Optional(ATTR_URL): vol.Url(),
|
|
vol.Optional(ATTR_STARTUP, default=AddonStartup.APPLICATION): vol.Coerce(
|
|
AddonStartup
|
|
),
|
|
vol.Optional(ATTR_BOOT, default=AddonBootConfig.AUTO): vol.Coerce(
|
|
AddonBootConfig
|
|
),
|
|
vol.Optional(ATTR_INIT, default=True): vol.Boolean(),
|
|
vol.Optional(ATTR_ADVANCED, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_STAGE, default=AddonStage.STABLE): vol.Coerce(AddonStage),
|
|
vol.Optional(ATTR_PORTS): docker_ports,
|
|
vol.Optional(ATTR_PORTS_DESCRIPTION): docker_ports_description,
|
|
vol.Optional(ATTR_WATCHDOG): vol.Match(
|
|
r"^(?:https?|\[PROTO:\w+\]|tcp):\/\/\[HOST\]:(\[PORT:\d+\]|\d+).*$"
|
|
),
|
|
vol.Optional(ATTR_WEBUI): vol.Match(
|
|
r"^(?:https?|\[PROTO:\w+\]):\/\/\[HOST\]:\[PORT:\d+\].*$"
|
|
),
|
|
vol.Optional(ATTR_INGRESS, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_INGRESS_PORT, default=8099): vol.Any(
|
|
network_port, vol.Equal(0)
|
|
),
|
|
vol.Optional(ATTR_INGRESS_ENTRY): str,
|
|
vol.Optional(ATTR_INGRESS_STREAM, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_PANEL_ICON, default="mdi:puzzle"): str,
|
|
vol.Optional(ATTR_PANEL_TITLE): str,
|
|
vol.Optional(ATTR_PANEL_ADMIN, default=True): vol.Boolean(),
|
|
vol.Optional(ATTR_HOMEASSISTANT): version_tag,
|
|
vol.Optional(ATTR_HOST_NETWORK, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_HOST_PID, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_HOST_IPC, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_HOST_UTS, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_HOST_DBUS, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_DEVICES): [str],
|
|
vol.Optional(ATTR_UDEV, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_TMPFS, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_MAP, default=list): [
|
|
vol.Schema(
|
|
{
|
|
vol.Required(ATTR_TYPE): vol.Coerce(MappingType),
|
|
vol.Optional(ATTR_READ_ONLY, default=True): bool,
|
|
vol.Optional(ATTR_PATH): str,
|
|
}
|
|
)
|
|
],
|
|
vol.Optional(ATTR_ENVIRONMENT): {vol.Match(r"\w*"): str},
|
|
vol.Optional(ATTR_PRIVILEGED): [vol.Coerce(Capabilities)],
|
|
vol.Optional(ATTR_APPARMOR, default=True): vol.Boolean(),
|
|
vol.Optional(ATTR_FULL_ACCESS, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_AUDIO, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_VIDEO, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_GPIO, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_USB, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_UART, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_DEVICETREE, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_KERNEL_MODULES, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_REALTIME, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_HASSIO_API, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_HASSIO_ROLE, default=ROLE_DEFAULT): vol.In(ROLE_ALL),
|
|
vol.Optional(ATTR_HOMEASSISTANT_API, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_STDIN, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_LEGACY, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_DOCKER_API, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_AUTH_API, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_SERVICES): [vol.Match(RE_SERVICE)],
|
|
vol.Optional(ATTR_DISCOVERY): [str],
|
|
vol.Optional(ATTR_BACKUP_EXCLUDE): [str],
|
|
vol.Optional(ATTR_BACKUP_PRE): str,
|
|
vol.Optional(ATTR_BACKUP_POST): str,
|
|
vol.Optional(ATTR_BACKUP, default=AddonBackupMode.HOT): vol.Coerce(
|
|
AddonBackupMode
|
|
),
|
|
vol.Optional(ATTR_OPTIONS, default={}): dict,
|
|
vol.Optional(ATTR_SCHEMA, default={}): vol.Any(
|
|
vol.Schema({str: SCHEMA_ELEMENT}),
|
|
False,
|
|
),
|
|
vol.Optional(ATTR_IMAGE): docker_image,
|
|
vol.Optional(ATTR_ULIMITS, default=dict): vol.Any(
|
|
{str: vol.Coerce(int)}, # Simple format: {name: limit}
|
|
{
|
|
str: vol.Any(
|
|
vol.Coerce(int), # Simple format for individual entries
|
|
vol.Schema(
|
|
{ # Detailed format for individual entries
|
|
vol.Required("soft"): vol.Coerce(int),
|
|
vol.Required("hard"): vol.Coerce(int),
|
|
}
|
|
),
|
|
)
|
|
},
|
|
),
|
|
vol.Optional(ATTR_TIMEOUT, default=10): vol.All(
|
|
vol.Coerce(int), vol.Range(min=10, max=300)
|
|
),
|
|
vol.Optional(ATTR_JOURNALD, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_BREAKING_VERSIONS, default=list): [version_tag],
|
|
},
|
|
extra=vol.REMOVE_EXTRA,
|
|
)
|
|
|
|
SCHEMA_ADDON_CONFIG = vol.All(
|
|
_migrate_addon_config(True), _warn_addon_config, _SCHEMA_ADDON_CONFIG
|
|
)
|
|
|
|
|
|
# pylint: disable=no-value-for-parameter
|
|
SCHEMA_BUILD_CONFIG = vol.Schema(
|
|
{
|
|
vol.Optional(ATTR_BUILD_FROM, default=dict): vol.Any(
|
|
vol.Match(RE_DOCKER_IMAGE_BUILD),
|
|
vol.Schema({vol.In(ARCH_ALL): vol.Match(RE_DOCKER_IMAGE_BUILD)}),
|
|
),
|
|
vol.Optional(ATTR_SQUASH, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_ARGS, default=dict): vol.Schema({str: str}),
|
|
vol.Optional(ATTR_LABELS, default=dict): vol.Schema({str: str}),
|
|
},
|
|
extra=vol.REMOVE_EXTRA,
|
|
)
|
|
|
|
SCHEMA_TRANSLATION_CONFIGURATION = vol.Schema(
|
|
{
|
|
vol.Required(ATTR_NAME): str,
|
|
vol.Optional(ATTR_DESCRIPTON): vol.Maybe(str),
|
|
vol.Optional(ATTR_FIELDS): {str: vol.Self},
|
|
},
|
|
extra=vol.REMOVE_EXTRA,
|
|
)
|
|
|
|
|
|
SCHEMA_ADDON_TRANSLATIONS = vol.Schema(
|
|
{
|
|
vol.Optional(ATTR_CONFIGURATION): {str: SCHEMA_TRANSLATION_CONFIGURATION},
|
|
vol.Optional(ATTR_NETWORK): {str: str},
|
|
},
|
|
extra=vol.REMOVE_EXTRA,
|
|
)
|
|
|
|
|
|
# pylint: disable=no-value-for-parameter
|
|
SCHEMA_ADDON_USER = vol.Schema(
|
|
{
|
|
vol.Required(ATTR_VERSION): version_tag,
|
|
vol.Optional(ATTR_IMAGE): docker_image,
|
|
vol.Optional(ATTR_UUID, default=lambda: uuid.uuid4().hex): uuid_match,
|
|
vol.Optional(ATTR_ACCESS_TOKEN): token,
|
|
vol.Optional(ATTR_INGRESS_TOKEN, default=secrets.token_urlsafe): str,
|
|
vol.Optional(ATTR_OPTIONS, default=dict): dict,
|
|
vol.Optional(ATTR_AUTO_UPDATE, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_BOOT): vol.Coerce(AddonBoot),
|
|
vol.Optional(ATTR_NETWORK): docker_ports,
|
|
vol.Optional(ATTR_AUDIO_OUTPUT): vol.Maybe(str),
|
|
vol.Optional(ATTR_AUDIO_INPUT): vol.Maybe(str),
|
|
vol.Optional(ATTR_PROTECTED, default=True): vol.Boolean(),
|
|
vol.Optional(ATTR_INGRESS_PANEL, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_WATCHDOG, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_SYSTEM_MANAGED, default=False): vol.Boolean(),
|
|
vol.Optional(ATTR_SYSTEM_MANAGED_CONFIG_ENTRY, default=None): vol.Maybe(str),
|
|
},
|
|
extra=vol.REMOVE_EXTRA,
|
|
)
|
|
|
|
SCHEMA_ADDON_SYSTEM = vol.All(
|
|
_migrate_addon_config(),
|
|
_SCHEMA_ADDON_CONFIG.extend(
|
|
{
|
|
vol.Required(ATTR_LOCATION): str,
|
|
vol.Required(ATTR_REPOSITORY): str,
|
|
vol.Required(ATTR_TRANSLATIONS, default=dict): {
|
|
str: SCHEMA_ADDON_TRANSLATIONS
|
|
},
|
|
}
|
|
),
|
|
)
|
|
|
|
|
|
SCHEMA_ADDONS_FILE = vol.Schema(
|
|
{
|
|
vol.Optional(ATTR_USER, default=dict): {str: SCHEMA_ADDON_USER},
|
|
vol.Optional(ATTR_SYSTEM, default=dict): {str: SCHEMA_ADDON_SYSTEM},
|
|
},
|
|
extra=vol.REMOVE_EXTRA,
|
|
)
|
|
|
|
|
|
SCHEMA_ADDON_BACKUP = vol.Schema(
|
|
{
|
|
vol.Required(ATTR_USER): SCHEMA_ADDON_USER,
|
|
vol.Required(ATTR_SYSTEM): SCHEMA_ADDON_SYSTEM,
|
|
vol.Required(ATTR_STATE): vol.Coerce(AddonState),
|
|
vol.Required(ATTR_VERSION): version_tag,
|
|
},
|
|
extra=vol.REMOVE_EXTRA,
|
|
)
|