Skip to content

Commit

Permalink
Add support for setting target path in map config (#4694)
Browse files Browse the repository at this point in the history
* Added support for setting addon target path in map config

* Updated addon target path mapping to use dataclass

* Added check before adding string folder maps

* Moved enum to addon/const, updated map_volumes logic, fixed test

* Removed log used for debugging

* Use more readable approach to determine addon_config_used

Co-authored-by: Mike Degatano <[email protected]>

* Use cleaner approach for checking volume config

Co-authored-by: Mike Degatano <[email protected]>

* Use dict syntax and ATTR_TYPE

Co-authored-by: Mike Degatano <[email protected]>

* Use coerce for validating mapping type

Co-authored-by: Mike Degatano <[email protected]>

* Default read_only to true in schema

Co-authored-by: Mike Degatano <[email protected]>

* Use ATTR_TYPE and ATTR_READ_ONLY instead of static strings

Co-authored-by: Mike Degatano <[email protected]>

* Use constants instead of in-line strings

Co-authored-by: Mike Degatano <[email protected]>

* Correct type for path

Co-authored-by: Mike Degatano <[email protected]>

* Added read_only and path constants

* Fixed small syntax error and added includes for constants

* Simplify logic for handling string and dict entries in map config

* Use ATTR_PATH instead of inline string

Co-authored-by: Mike Degatano <[email protected]>

* Add missing ATTR_PATH reference

* Moved FolderMapping dataclass to data.py

* Fix edge case where "data" map type is used but optional path is not set

* Move FolderMapping dataclass to configuration.py to prevent circular reference

---------

Co-authored-by: Jeff Oakley <[email protected]>
Co-authored-by: Mike Degatano <[email protected]>
Co-authored-by: Pascal Vizeli <[email protected]>
  • Loading branch information
4 people authored Dec 27, 2023
1 parent 2c09e79 commit e08c8ca
Show file tree
Hide file tree
Showing 8 changed files with 173 additions and 71 deletions.
4 changes: 2 additions & 2 deletions supervisor/addons/addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
ATTR_VERSION,
ATTR_WATCHDOG,
DNS_SUFFIX,
MAP_ADDON_CONFIG,
AddonBoot,
AddonStartup,
AddonState,
Expand Down Expand Up @@ -85,6 +84,7 @@
WATCHDOG_THROTTLE_MAX_CALLS,
WATCHDOG_THROTTLE_PERIOD,
AddonBackupMode,
MappingType,
)
from .model import AddonModel, Data
from .options import AddonOptions
Expand Down Expand Up @@ -467,7 +467,7 @@ def path_extern_data(self) -> PurePath:
@property
def addon_config_used(self) -> bool:
"""Add-on is using its public config folder."""
return MAP_ADDON_CONFIG in self.map_volumes
return MappingType.ADDON_CONFIG in self.map_volumes

@property
def path_config(self) -> Path:
Expand Down
11 changes: 11 additions & 0 deletions supervisor/addons/configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""Confgiuration Objects for Addon Config."""

from dataclasses import dataclass


@dataclass(slots=True)
class FolderMapping:
"""Represent folder mapping configuration."""

path: str | None
read_only: bool
17 changes: 17 additions & 0 deletions supervisor/addons/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,25 @@ class AddonBackupMode(StrEnum):
COLD = "cold"


class MappingType(StrEnum):
"""Mapping type of an Add-on Folder."""

DATA = "data"
CONFIG = "config"
SSL = "ssl"
ADDONS = "addons"
BACKUP = "backup"
SHARE = "share"
MEDIA = "media"
HOMEASSISTANT_CONFIG = "homeassistant_config"
ALL_ADDON_CONFIGS = "all_addon_configs"
ADDON_CONFIG = "addon_config"


ATTR_BACKUP = "backup"
ATTR_CODENOTARY = "codenotary"
ATTR_READ_ONLY = "read_only"
ATTR_PATH = "path"
WATCHDOG_RETRY_SECONDS = 10
WATCHDOG_MAX_ATTEMPTS = 5
WATCHDOG_THROTTLE_PERIOD = timedelta(minutes=30)
Expand Down
24 changes: 16 additions & 8 deletions supervisor/addons/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
ATTR_TIMEOUT,
ATTR_TMPFS,
ATTR_TRANSLATIONS,
ATTR_TYPE,
ATTR_UART,
ATTR_UDEV,
ATTR_URL,
Expand All @@ -86,9 +87,17 @@
from ..jobs.const import JOB_GROUP_ADDON
from ..jobs.job_group import JobGroup
from ..utils import version_is_new_enough
from .const import ATTR_BACKUP, ATTR_CODENOTARY, AddonBackupMode
from .configuration import FolderMapping
from .const import (
ATTR_BACKUP,
ATTR_CODENOTARY,
ATTR_PATH,
ATTR_READ_ONLY,
AddonBackupMode,
MappingType,
)
from .options import AddonOptions, UiOptions
from .validate import RE_SERVICE, RE_VOLUME
from .validate import RE_SERVICE

_LOGGER: logging.Logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -538,14 +547,13 @@ def need_build(self) -> bool:
return ATTR_IMAGE not in self.data

@property
def map_volumes(self) -> dict[str, bool]:
"""Return a dict of {volume: read-only} from add-on."""
def map_volumes(self) -> dict[MappingType, FolderMapping]:
"""Return a dict of {MappingType: FolderMapping} from add-on."""
volumes = {}
for volume in self.data[ATTR_MAP]:
result = RE_VOLUME.match(volume)
if not result:
continue
volumes[result.group(1)] = result.group(2) != "rw"
volumes[MappingType(volume[ATTR_TYPE])] = FolderMapping(
volume.get(ATTR_PATH), volume[ATTR_READ_ONLY]
)

return volumes

Expand Down
61 changes: 47 additions & 14 deletions supervisor/addons/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
ATTR_TIMEOUT,
ATTR_TMPFS,
ATTR_TRANSLATIONS,
ATTR_TYPE,
ATTR_UART,
ATTR_UDEV,
ATTR_URL,
Expand All @@ -91,9 +92,6 @@
ATTR_VIDEO,
ATTR_WATCHDOG,
ATTR_WEBUI,
MAP_ADDON_CONFIG,
MAP_CONFIG,
MAP_HOMEASSISTANT_CONFIG,
ROLE_ALL,
ROLE_DEFAULT,
AddonBoot,
Expand All @@ -112,13 +110,21 @@
uuid_match,
version_tag,
)
from .const import ATTR_BACKUP, ATTR_CODENOTARY, RE_SLUG, AddonBackupMode
from .const import (
ATTR_BACKUP,
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"^(config|ssl|addons|backup|share|media|homeassistant_config|all_addon_configs|addon_config)(?::(rw|ro))?$"
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)$")

Expand Down Expand Up @@ -266,26 +272,45 @@ def _migrate(config: dict[str, Any]):
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):
volumes.append(entry)
if isinstance(entry, str):
result = RE_VOLUME.match(entry)
if not result:
continue
volumes.append(
{
ATTR_TYPE: result.group(1),
ATTR_READ_ONLY: result.group(2) != "rw",
}
)

if volumes:
config[ATTR_MAP] = volumes

# 2023-10 "config" became "homeassistant" so /config can be used for addon's public config
volumes = [RE_VOLUME.match(entry) for entry in config.get(ATTR_MAP, [])]
if any(volume and volume.group(1) == MAP_CONFIG for volume in volumes):
if any(volume[ATTR_TYPE] == MappingType.CONFIG for volume in volumes):
if any(
volume
and volume.group(1) in {MAP_ADDON_CONFIG, MAP_HOMEASSISTANT_CONFIG}
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",
MAP_ADDON_CONFIG,
MAP_HOMEASSISTANT_CONFIG,
MAP_CONFIG,
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",
MAP_CONFIG,
MAP_HOMEASSISTANT_CONFIG,
MappingType.CONFIG,
MappingType.HOMEASSISTANT_CONFIG,
name,
)

Expand Down Expand Up @@ -337,7 +362,15 @@ def _migrate(config: dict[str, Any]):
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.Match(RE_VOLUME)],
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(),
Expand Down
11 changes: 0 additions & 11 deletions supervisor/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,17 +345,6 @@
NEED_SERVICE = "need"
WANT_SERVICE = "want"


MAP_CONFIG = "config"
MAP_SSL = "ssl"
MAP_ADDONS = "addons"
MAP_BACKUP = "backup"
MAP_SHARE = "share"
MAP_MEDIA = "media"
MAP_HOMEASSISTANT_CONFIG = "homeassistant_config"
MAP_ALL_ADDON_CONFIGS = "all_addon_configs"
MAP_ADDON_CONFIG = "addon_config"

ARCH_ARMHF = "armhf"
ARCH_ARMV7 = "armv7"
ARCH_AARCH64 = "aarch64"
Expand Down
Loading

0 comments on commit e08c8ca

Please sign in to comment.