mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-10 10:46:29 +00:00
Add support for setting target path in map config (#4694)
* 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 <michael.degatano@gmail.com> * Use cleaner approach for checking volume config Co-authored-by: Mike Degatano <michael.degatano@gmail.com> * Use dict syntax and ATTR_TYPE Co-authored-by: Mike Degatano <michael.degatano@gmail.com> * Use coerce for validating mapping type Co-authored-by: Mike Degatano <michael.degatano@gmail.com> * Default read_only to true in schema Co-authored-by: Mike Degatano <michael.degatano@gmail.com> * Use ATTR_TYPE and ATTR_READ_ONLY instead of static strings Co-authored-by: Mike Degatano <michael.degatano@gmail.com> * Use constants instead of in-line strings Co-authored-by: Mike Degatano <michael.degatano@gmail.com> * Correct type for path Co-authored-by: Mike Degatano <michael.degatano@gmail.com> * 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 <michael.degatano@gmail.com> * 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 <jeff.oakley@LearningCircleSoftware.com> Co-authored-by: Mike Degatano <michael.degatano@gmail.com> Co-authored-by: Pascal Vizeli <pvizeli@syshack.ch>
This commit is contained in:
parent
2c09e7929f
commit
e08c8ca26d
@ -48,7 +48,6 @@ from ..const import (
|
|||||||
ATTR_VERSION,
|
ATTR_VERSION,
|
||||||
ATTR_WATCHDOG,
|
ATTR_WATCHDOG,
|
||||||
DNS_SUFFIX,
|
DNS_SUFFIX,
|
||||||
MAP_ADDON_CONFIG,
|
|
||||||
AddonBoot,
|
AddonBoot,
|
||||||
AddonStartup,
|
AddonStartup,
|
||||||
AddonState,
|
AddonState,
|
||||||
@ -85,6 +84,7 @@ from .const import (
|
|||||||
WATCHDOG_THROTTLE_MAX_CALLS,
|
WATCHDOG_THROTTLE_MAX_CALLS,
|
||||||
WATCHDOG_THROTTLE_PERIOD,
|
WATCHDOG_THROTTLE_PERIOD,
|
||||||
AddonBackupMode,
|
AddonBackupMode,
|
||||||
|
MappingType,
|
||||||
)
|
)
|
||||||
from .model import AddonModel, Data
|
from .model import AddonModel, Data
|
||||||
from .options import AddonOptions
|
from .options import AddonOptions
|
||||||
@ -467,7 +467,7 @@ class Addon(AddonModel):
|
|||||||
@property
|
@property
|
||||||
def addon_config_used(self) -> bool:
|
def addon_config_used(self) -> bool:
|
||||||
"""Add-on is using its public config folder."""
|
"""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
|
@property
|
||||||
def path_config(self) -> Path:
|
def path_config(self) -> Path:
|
||||||
|
11
supervisor/addons/configuration.py
Normal file
11
supervisor/addons/configuration.py
Normal file
@ -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
|
@ -12,8 +12,25 @@ class AddonBackupMode(StrEnum):
|
|||||||
COLD = "cold"
|
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_BACKUP = "backup"
|
||||||
ATTR_CODENOTARY = "codenotary"
|
ATTR_CODENOTARY = "codenotary"
|
||||||
|
ATTR_READ_ONLY = "read_only"
|
||||||
|
ATTR_PATH = "path"
|
||||||
WATCHDOG_RETRY_SECONDS = 10
|
WATCHDOG_RETRY_SECONDS = 10
|
||||||
WATCHDOG_MAX_ATTEMPTS = 5
|
WATCHDOG_MAX_ATTEMPTS = 5
|
||||||
WATCHDOG_THROTTLE_PERIOD = timedelta(minutes=30)
|
WATCHDOG_THROTTLE_PERIOD = timedelta(minutes=30)
|
||||||
|
@ -65,6 +65,7 @@ from ..const import (
|
|||||||
ATTR_TIMEOUT,
|
ATTR_TIMEOUT,
|
||||||
ATTR_TMPFS,
|
ATTR_TMPFS,
|
||||||
ATTR_TRANSLATIONS,
|
ATTR_TRANSLATIONS,
|
||||||
|
ATTR_TYPE,
|
||||||
ATTR_UART,
|
ATTR_UART,
|
||||||
ATTR_UDEV,
|
ATTR_UDEV,
|
||||||
ATTR_URL,
|
ATTR_URL,
|
||||||
@ -86,9 +87,17 @@ from ..exceptions import AddonsNotSupportedError
|
|||||||
from ..jobs.const import JOB_GROUP_ADDON
|
from ..jobs.const import JOB_GROUP_ADDON
|
||||||
from ..jobs.job_group import JobGroup
|
from ..jobs.job_group import JobGroup
|
||||||
from ..utils import version_is_new_enough
|
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 .options import AddonOptions, UiOptions
|
||||||
from .validate import RE_SERVICE, RE_VOLUME
|
from .validate import RE_SERVICE
|
||||||
|
|
||||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -538,14 +547,13 @@ class AddonModel(JobGroup, ABC):
|
|||||||
return ATTR_IMAGE not in self.data
|
return ATTR_IMAGE not in self.data
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def map_volumes(self) -> dict[str, bool]:
|
def map_volumes(self) -> dict[MappingType, FolderMapping]:
|
||||||
"""Return a dict of {volume: read-only} from add-on."""
|
"""Return a dict of {MappingType: FolderMapping} from add-on."""
|
||||||
volumes = {}
|
volumes = {}
|
||||||
for volume in self.data[ATTR_MAP]:
|
for volume in self.data[ATTR_MAP]:
|
||||||
result = RE_VOLUME.match(volume)
|
volumes[MappingType(volume[ATTR_TYPE])] = FolderMapping(
|
||||||
if not result:
|
volume.get(ATTR_PATH), volume[ATTR_READ_ONLY]
|
||||||
continue
|
)
|
||||||
volumes[result.group(1)] = result.group(2) != "rw"
|
|
||||||
|
|
||||||
return volumes
|
return volumes
|
||||||
|
|
||||||
|
@ -81,6 +81,7 @@ from ..const import (
|
|||||||
ATTR_TIMEOUT,
|
ATTR_TIMEOUT,
|
||||||
ATTR_TMPFS,
|
ATTR_TMPFS,
|
||||||
ATTR_TRANSLATIONS,
|
ATTR_TRANSLATIONS,
|
||||||
|
ATTR_TYPE,
|
||||||
ATTR_UART,
|
ATTR_UART,
|
||||||
ATTR_UDEV,
|
ATTR_UDEV,
|
||||||
ATTR_URL,
|
ATTR_URL,
|
||||||
@ -91,9 +92,6 @@ from ..const import (
|
|||||||
ATTR_VIDEO,
|
ATTR_VIDEO,
|
||||||
ATTR_WATCHDOG,
|
ATTR_WATCHDOG,
|
||||||
ATTR_WEBUI,
|
ATTR_WEBUI,
|
||||||
MAP_ADDON_CONFIG,
|
|
||||||
MAP_CONFIG,
|
|
||||||
MAP_HOMEASSISTANT_CONFIG,
|
|
||||||
ROLE_ALL,
|
ROLE_ALL,
|
||||||
ROLE_DEFAULT,
|
ROLE_DEFAULT,
|
||||||
AddonBoot,
|
AddonBoot,
|
||||||
@ -112,13 +110,21 @@ from ..validate import (
|
|||||||
uuid_match,
|
uuid_match,
|
||||||
version_tag,
|
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
|
from .options import RE_SCHEMA_ELEMENT
|
||||||
|
|
||||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
RE_VOLUME = re.compile(
|
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)$")
|
RE_SERVICE = re.compile(r"^(?P<service>mqtt|mysql):(?P<rights>provide|want|need)$")
|
||||||
|
|
||||||
@ -266,26 +272,45 @@ def _migrate_addon_config(protocol=False):
|
|||||||
name,
|
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
|
# 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[ATTR_TYPE] == MappingType.CONFIG for volume in volumes):
|
||||||
if any(volume and volume.group(1) == MAP_CONFIG for volume in volumes):
|
|
||||||
if any(
|
if any(
|
||||||
volume
|
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
|
for volume in volumes
|
||||||
):
|
):
|
||||||
_LOGGER.warning(
|
_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",
|
"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,
|
MappingType.ADDON_CONFIG,
|
||||||
MAP_HOMEASSISTANT_CONFIG,
|
MappingType.HOMEASSISTANT_CONFIG,
|
||||||
MAP_CONFIG,
|
MappingType.CONFIG,
|
||||||
name,
|
name,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Add-on config using deprecated map option '%s' instead of '%s'. Please report this to the maintainer of %s",
|
"Add-on config using deprecated map option '%s' instead of '%s'. Please report this to the maintainer of %s",
|
||||||
MAP_CONFIG,
|
MappingType.CONFIG,
|
||||||
MAP_HOMEASSISTANT_CONFIG,
|
MappingType.HOMEASSISTANT_CONFIG,
|
||||||
name,
|
name,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -337,7 +362,15 @@ _SCHEMA_ADDON_CONFIG = vol.Schema(
|
|||||||
vol.Optional(ATTR_DEVICES): [str],
|
vol.Optional(ATTR_DEVICES): [str],
|
||||||
vol.Optional(ATTR_UDEV, default=False): vol.Boolean(),
|
vol.Optional(ATTR_UDEV, default=False): vol.Boolean(),
|
||||||
vol.Optional(ATTR_TMPFS, 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_ENVIRONMENT): {vol.Match(r"\w*"): str},
|
||||||
vol.Optional(ATTR_PRIVILEGED): [vol.Coerce(Capabilities)],
|
vol.Optional(ATTR_PRIVILEGED): [vol.Coerce(Capabilities)],
|
||||||
vol.Optional(ATTR_APPARMOR, default=True): vol.Boolean(),
|
vol.Optional(ATTR_APPARMOR, default=True): vol.Boolean(),
|
||||||
|
@ -345,17 +345,6 @@ PROVIDE_SERVICE = "provide"
|
|||||||
NEED_SERVICE = "need"
|
NEED_SERVICE = "need"
|
||||||
WANT_SERVICE = "want"
|
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_ARMHF = "armhf"
|
||||||
ARCH_ARMV7 = "armv7"
|
ARCH_ARMV7 = "armv7"
|
||||||
ARCH_AARCH64 = "aarch64"
|
ARCH_AARCH64 = "aarch64"
|
||||||
|
@ -15,18 +15,10 @@ from docker.types import Mount
|
|||||||
import requests
|
import requests
|
||||||
|
|
||||||
from ..addons.build import AddonBuild
|
from ..addons.build import AddonBuild
|
||||||
|
from ..addons.const import MappingType
|
||||||
from ..bus import EventListener
|
from ..bus import EventListener
|
||||||
from ..const import (
|
from ..const import (
|
||||||
DOCKER_CPU_RUNTIME_ALLOCATION,
|
DOCKER_CPU_RUNTIME_ALLOCATION,
|
||||||
MAP_ADDON_CONFIG,
|
|
||||||
MAP_ADDONS,
|
|
||||||
MAP_ALL_ADDON_CONFIGS,
|
|
||||||
MAP_BACKUP,
|
|
||||||
MAP_CONFIG,
|
|
||||||
MAP_HOMEASSISTANT_CONFIG,
|
|
||||||
MAP_MEDIA,
|
|
||||||
MAP_SHARE,
|
|
||||||
MAP_SSL,
|
|
||||||
SECURITY_DISABLE,
|
SECURITY_DISABLE,
|
||||||
SECURITY_PROFILE,
|
SECURITY_PROFILE,
|
||||||
SYSTEMD_JOURNAL_PERSISTENT,
|
SYSTEMD_JOURNAL_PERSISTENT,
|
||||||
@ -332,24 +324,28 @@ class DockerAddon(DockerInterface):
|
|||||||
"""Return mounts for container."""
|
"""Return mounts for container."""
|
||||||
addon_mapping = self.addon.map_volumes
|
addon_mapping = self.addon.map_volumes
|
||||||
|
|
||||||
|
target_data_path = ""
|
||||||
|
if MappingType.DATA in addon_mapping:
|
||||||
|
target_data_path = addon_mapping[MappingType.DATA].path
|
||||||
|
|
||||||
mounts = [
|
mounts = [
|
||||||
MOUNT_DEV,
|
MOUNT_DEV,
|
||||||
Mount(
|
Mount(
|
||||||
type=MountType.BIND,
|
type=MountType.BIND,
|
||||||
source=self.addon.path_extern_data.as_posix(),
|
source=self.addon.path_extern_data.as_posix(),
|
||||||
target="/data",
|
target=target_data_path or "/data",
|
||||||
read_only=False,
|
read_only=False,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
# setup config mappings
|
# setup config mappings
|
||||||
if MAP_CONFIG in addon_mapping:
|
if MappingType.CONFIG in addon_mapping:
|
||||||
mounts.append(
|
mounts.append(
|
||||||
Mount(
|
Mount(
|
||||||
type=MountType.BIND,
|
type=MountType.BIND,
|
||||||
source=self.sys_config.path_extern_homeassistant.as_posix(),
|
source=self.sys_config.path_extern_homeassistant.as_posix(),
|
||||||
target="/config",
|
target=addon_mapping[MappingType.CONFIG].path or "/config",
|
||||||
read_only=addon_mapping[MAP_CONFIG],
|
read_only=addon_mapping[MappingType.CONFIG].read_only,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -360,80 +356,85 @@ class DockerAddon(DockerInterface):
|
|||||||
Mount(
|
Mount(
|
||||||
type=MountType.BIND,
|
type=MountType.BIND,
|
||||||
source=self.addon.path_extern_config.as_posix(),
|
source=self.addon.path_extern_config.as_posix(),
|
||||||
target="/config",
|
target=addon_mapping[MappingType.ADDON_CONFIG].path
|
||||||
read_only=addon_mapping[MAP_ADDON_CONFIG],
|
or "/config",
|
||||||
|
read_only=addon_mapping[MappingType.ADDON_CONFIG].read_only,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Map Home Assistant config in new way
|
# Map Home Assistant config in new way
|
||||||
if MAP_HOMEASSISTANT_CONFIG in addon_mapping:
|
if MappingType.HOMEASSISTANT_CONFIG in addon_mapping:
|
||||||
mounts.append(
|
mounts.append(
|
||||||
Mount(
|
Mount(
|
||||||
type=MountType.BIND,
|
type=MountType.BIND,
|
||||||
source=self.sys_config.path_extern_homeassistant.as_posix(),
|
source=self.sys_config.path_extern_homeassistant.as_posix(),
|
||||||
target="/homeassistant",
|
target=addon_mapping[MappingType.HOMEASSISTANT_CONFIG].path
|
||||||
read_only=addon_mapping[MAP_HOMEASSISTANT_CONFIG],
|
or "/homeassistant",
|
||||||
|
read_only=addon_mapping[
|
||||||
|
MappingType.HOMEASSISTANT_CONFIG
|
||||||
|
].read_only,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if MAP_ALL_ADDON_CONFIGS in addon_mapping:
|
if MappingType.ALL_ADDON_CONFIGS in addon_mapping:
|
||||||
mounts.append(
|
mounts.append(
|
||||||
Mount(
|
Mount(
|
||||||
type=MountType.BIND,
|
type=MountType.BIND,
|
||||||
source=self.sys_config.path_extern_addon_configs.as_posix(),
|
source=self.sys_config.path_extern_addon_configs.as_posix(),
|
||||||
target="/addon_configs",
|
target=addon_mapping[MappingType.ALL_ADDON_CONFIGS].path
|
||||||
read_only=addon_mapping[MAP_ALL_ADDON_CONFIGS],
|
or "/addon_configs",
|
||||||
|
read_only=addon_mapping[MappingType.ALL_ADDON_CONFIGS].read_only,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if MAP_SSL in addon_mapping:
|
if MappingType.SSL in addon_mapping:
|
||||||
mounts.append(
|
mounts.append(
|
||||||
Mount(
|
Mount(
|
||||||
type=MountType.BIND,
|
type=MountType.BIND,
|
||||||
source=self.sys_config.path_extern_ssl.as_posix(),
|
source=self.sys_config.path_extern_ssl.as_posix(),
|
||||||
target="/ssl",
|
target=addon_mapping[MappingType.SSL].path or "/ssl",
|
||||||
read_only=addon_mapping[MAP_SSL],
|
read_only=addon_mapping[MappingType.SSL].read_only,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if MAP_ADDONS in addon_mapping:
|
if MappingType.ADDONS in addon_mapping:
|
||||||
mounts.append(
|
mounts.append(
|
||||||
Mount(
|
Mount(
|
||||||
type=MountType.BIND,
|
type=MountType.BIND,
|
||||||
source=self.sys_config.path_extern_addons_local.as_posix(),
|
source=self.sys_config.path_extern_addons_local.as_posix(),
|
||||||
target="/addons",
|
target=addon_mapping[MappingType.ADDONS].path or "/addons",
|
||||||
read_only=addon_mapping[MAP_ADDONS],
|
read_only=addon_mapping[MappingType.ADDONS].read_only,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if MAP_BACKUP in addon_mapping:
|
if MappingType.BACKUP in addon_mapping:
|
||||||
mounts.append(
|
mounts.append(
|
||||||
Mount(
|
Mount(
|
||||||
type=MountType.BIND,
|
type=MountType.BIND,
|
||||||
source=self.sys_config.path_extern_backup.as_posix(),
|
source=self.sys_config.path_extern_backup.as_posix(),
|
||||||
target="/backup",
|
target=addon_mapping[MappingType.BACKUP].path or "/backup",
|
||||||
read_only=addon_mapping[MAP_BACKUP],
|
read_only=addon_mapping[MappingType.BACKUP].read_only,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if MAP_SHARE in addon_mapping:
|
if MappingType.SHARE in addon_mapping:
|
||||||
mounts.append(
|
mounts.append(
|
||||||
Mount(
|
Mount(
|
||||||
type=MountType.BIND,
|
type=MountType.BIND,
|
||||||
source=self.sys_config.path_extern_share.as_posix(),
|
source=self.sys_config.path_extern_share.as_posix(),
|
||||||
target="/share",
|
target=addon_mapping[MappingType.SHARE].path or "/share",
|
||||||
read_only=addon_mapping[MAP_SHARE],
|
read_only=addon_mapping[MappingType.SHARE].read_only,
|
||||||
propagation=PropagationMode.RSLAVE,
|
propagation=PropagationMode.RSLAVE,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if MAP_MEDIA in addon_mapping:
|
if MappingType.MEDIA in addon_mapping:
|
||||||
mounts.append(
|
mounts.append(
|
||||||
Mount(
|
Mount(
|
||||||
type=MountType.BIND,
|
type=MountType.BIND,
|
||||||
source=self.sys_config.path_extern_media.as_posix(),
|
source=self.sys_config.path_extern_media.as_posix(),
|
||||||
target="/media",
|
target=addon_mapping[MappingType.MEDIA].path or "/media",
|
||||||
read_only=addon_mapping[MAP_MEDIA],
|
read_only=addon_mapping[MappingType.MEDIA].read_only,
|
||||||
propagation=PropagationMode.RSLAVE,
|
propagation=PropagationMode.RSLAVE,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -201,6 +201,49 @@ def test_addon_map_addon_config_folder(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_addon_map_addon_config_folder_with_custom_target(
|
||||||
|
coresys: CoreSys, addonsdata_system: dict[str, Data], path_extern
|
||||||
|
):
|
||||||
|
"""Test mounts for addon which maps its own config folder and sets target path."""
|
||||||
|
config = load_json_fixture("addon-config-map-addon_config.json")
|
||||||
|
config["map"].remove("addon_config")
|
||||||
|
config["map"].append(
|
||||||
|
{"type": "addon_config", "read_only": False, "path": "/custom/target/path"}
|
||||||
|
)
|
||||||
|
docker_addon = get_docker_addon(coresys, addonsdata_system, config)
|
||||||
|
|
||||||
|
# Addon config folder included
|
||||||
|
assert (
|
||||||
|
Mount(
|
||||||
|
type="bind",
|
||||||
|
source=docker_addon.addon.path_extern_config.as_posix(),
|
||||||
|
target="/custom/target/path",
|
||||||
|
read_only=False,
|
||||||
|
)
|
||||||
|
in docker_addon.mounts
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_addon_map_data_folder_with_custom_target(
|
||||||
|
coresys: CoreSys, addonsdata_system: dict[str, Data], path_extern
|
||||||
|
):
|
||||||
|
"""Test mounts for addon which sets target path for data folder."""
|
||||||
|
config = load_json_fixture("addon-config-map-addon_config.json")
|
||||||
|
config["map"].append({"type": "data", "path": "/custom/data/path"})
|
||||||
|
docker_addon = get_docker_addon(coresys, addonsdata_system, config)
|
||||||
|
|
||||||
|
# Addon config folder included
|
||||||
|
assert (
|
||||||
|
Mount(
|
||||||
|
type="bind",
|
||||||
|
source=docker_addon.addon.path_extern_data.as_posix(),
|
||||||
|
target="/custom/data/path",
|
||||||
|
read_only=False,
|
||||||
|
)
|
||||||
|
in docker_addon.mounts
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_addon_ignore_on_config_map(
|
def test_addon_ignore_on_config_map(
|
||||||
coresys: CoreSys, addonsdata_system: dict[str, Data], path_extern
|
coresys: CoreSys, addonsdata_system: dict[str, Data], path_extern
|
||||||
):
|
):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user