Skip to content

Commit

Permalink
Rename map values and add addon_config
Browse files Browse the repository at this point in the history
  • Loading branch information
mdegat01 committed Oct 26, 2023
1 parent 9951ced commit 88fa7ef
Show file tree
Hide file tree
Showing 11 changed files with 146 additions and 113 deletions.
2 changes: 1 addition & 1 deletion supervisor/addons/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ async def install(self, slug: str) -> None:
)
addon.path_data.mkdir()

if not addon.path_config.is_dir():
if addon.addon_config_used and not addon.path_config.is_dir():
_LOGGER.info(
"Creating Home Assistant add-on config folder %s", addon.path_config
)
Expand Down
32 changes: 14 additions & 18 deletions supervisor/addons/addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@
ATTR_VERSION,
ATTR_WATCHDOG,
DNS_SUFFIX,
MAP_CONFIG,
MAP_HOMEASSISTANT,
MAP_ADDON_CONFIG,
AddonBoot,
AddonStartup,
AddonState,
Expand Down Expand Up @@ -172,15 +171,6 @@ def in_progress(self) -> bool:

async def load(self) -> None:
"""Async initialize of object."""
if MAP_CONFIG in self.map_volumes:
_LOGGER.warning(
"Add-on '%s' is using deprecated map option '%s' instead of '%s', please report an issue to the add-on developer at: %s",
self.name,
MAP_CONFIG,
MAP_HOMEASSISTANT,
self.url,
)

self._listeners.append(
self.sys_bus.register_event(
BusEvent.DOCKER_CONTAINER_STATE_CHANGE, self.container_state_changed
Expand Down Expand Up @@ -464,6 +454,11 @@ def path_extern_data(self) -> PurePath:
"""Return add-on data path external for Docker."""
return PurePath(self.sys_config.path_extern_addons_data, self.slug)

@property
def addon_config_used(self) -> bool:
"""Add-on is using its public config folder."""
return MAP_ADDON_CONFIG in self.map_volumes

@property
def path_config(self) -> Path:
"""Return add-on config path inside Supervisor."""
Expand Down Expand Up @@ -887,12 +882,13 @@ def _write_tarfile():
)

# Backup config
atomic_contents_add(
backup,
self.path_config,
excludes=self.backup_exclude,
arcname="config",
)
if self.addon_config_used:
atomic_contents_add(
backup,
self.path_config,
excludes=self.backup_exclude,
arcname="config",
)

is_running = await self.begin_backup()
try:
Expand Down Expand Up @@ -994,7 +990,7 @@ def _restore_data():
temp_config = Path(temp, "config")
if temp_config.is_dir():
shutil.copytree(temp_config, self.path_config, symlinks=True)
else:
elif self.addon_config_used:
self.path_config.mkdir()

_LOGGER.info("Restoring data and config for addon %s", self.slug)
Expand Down
28 changes: 27 additions & 1 deletion supervisor/addons/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@
ATTR_VIDEO,
ATTR_WATCHDOG,
ATTR_WEBUI,
MAP_ADDON_CONFIG,
MAP_CONFIG,
MAP_HOMEASSISTANT_CONFIG,
ROLE_ALL,
ROLE_DEFAULT,
AddonBoot,
Expand All @@ -115,7 +118,7 @@
_LOGGER: logging.Logger = logging.getLogger(__name__)

RE_VOLUME = re.compile(
r"^(config|ssl|addons|backup|share|media|homeassistant|addon_configs)(?::(rw|ro))?$"
r"^(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 @@ -262,6 +265,29 @@ def _migrate(config: dict[str, Any]):
name,
)

# 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) for volume in volumes):
if any(
volume
and volume.group(1) in {MAP_ADDON_CONFIG, MAP_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,
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,
name,
)

return config

return _migrate
Expand Down
5 changes: 3 additions & 2 deletions supervisor/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,9 @@
MAP_BACKUP = "backup"
MAP_SHARE = "share"
MAP_MEDIA = "media"
MAP_HOMEASSISTANT = "homeassistant"
MAP_ADDON_CONFIGS = "addon_configs"
MAP_HOMEASSISTANT_CONFIG = "homeassistant_config"
MAP_ALL_ADDON_CONFIGS = "all_addon_configs"
MAP_ADDON_CONFIG = "addon_config"

ARCH_ARMHF = "armhf"
ARCH_ARMV7 = "armv7"
Expand Down
32 changes: 17 additions & 15 deletions supervisor/docker/addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@
from ..bus import EventListener
from ..const import (
DOCKER_CPU_RUNTIME_ALLOCATION,
MAP_ADDON_CONFIGS,
MAP_ADDON_CONFIG,
MAP_ADDONS,
MAP_ALL_ADDON_CONFIGS,
MAP_BACKUP,
MAP_CONFIG,
MAP_HOMEASSISTANT,
MAP_HOMEASSISTANT_CONFIG,
MAP_MEDIA,
MAP_SHARE,
MAP_SSL,
Expand Down Expand Up @@ -352,35 +353,36 @@ def mounts(self) -> list[Mount]:
)
)

# Map addon's public config folder if not using deprecated config option
else:
mounts.append(
Mount(
type=MountType.BIND,
source=self.addon.path_extern_config.as_posix(),
target="/config",
read_only=False,
# Map addon's public config folder if not using deprecated config option
if self.addon.addon_config_used:
mounts.append(
Mount(
type=MountType.BIND,
source=self.addon.path_extern_config.as_posix(),
target="/config",
read_only=addon_mapping[MAP_ADDON_CONFIG],
)
)
)

# Map Home Assistant config in new way if requested
if MAP_HOMEASSISTANT in addon_mapping:
# Map Home Assistant config in new way
if MAP_HOMEASSISTANT_CONFIG in addon_mapping:
mounts.append(
Mount(
type=MountType.BIND,
source=self.sys_config.path_extern_homeassistant.as_posix(),
target="/homeassistant",
read_only=addon_mapping[MAP_HOMEASSISTANT],
read_only=addon_mapping[MAP_HOMEASSISTANT_CONFIG],
)
)

if MAP_ADDON_CONFIGS in addon_mapping:
if MAP_ALL_ADDON_CONFIGS in addon_mapping:
mounts.append(
Mount(
type=MountType.BIND,
source=self.sys_config.path_extern_addon_configs.as_posix(),
target="/addon_configs",
read_only=addon_mapping[MAP_ADDON_CONFIGS],
read_only=addon_mapping[MAP_ALL_ADDON_CONFIGS],
)
)

Expand Down
7 changes: 0 additions & 7 deletions tests/addons/test_addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,6 @@ async def test_backup(
"""Test backing up an addon."""
container.status = status
install_addon_ssh.path_data.mkdir()
install_addon_ssh.path_config.mkdir()
await install_addon_ssh.load()

tarfile = SecureTarFile(coresys.config.path_tmp / "test.tar.gz", "w")
Expand All @@ -435,7 +434,6 @@ async def test_backup_with_pre_post_command(
container.status = "running"
container.exec_run.return_value = (0, None)
install_addon_ssh.path_data.mkdir()
install_addon_ssh.path_config.mkdir()
await install_addon_ssh.load()

tarfile = SecureTarFile(coresys.config.path_tmp / "test.tar.gz", "w")
Expand Down Expand Up @@ -500,7 +498,6 @@ async def test_backup_cold_mode(
"""Test backing up an addon in cold mode."""
container.status = status
install_addon_ssh.path_data.mkdir()
install_addon_ssh.path_config.mkdir()
await install_addon_ssh.load()

tarfile = SecureTarFile(coresys.config.path_tmp / "test.tar.gz", "w")
Expand All @@ -525,7 +522,6 @@ async def test_backup_cold_mode_with_watchdog(
container.status = "running"
install_addon_ssh.watchdog = True
install_addon_ssh.path_data.mkdir()
install_addon_ssh.path_config.mkdir()
await install_addon_ssh.load()

# Simulate stop firing the docker event for stopped container like it normally would
Expand Down Expand Up @@ -563,7 +559,6 @@ async def test_restore(
"""Test restoring an addon."""
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
install_addon_ssh.path_data.mkdir()
install_addon_ssh.path_config.mkdir()
await install_addon_ssh.load()

tarfile = SecureTarFile(get_fixture_path(f"backup_local_ssh_{status}.tar.gz"), "r")
Expand All @@ -586,7 +581,6 @@ async def test_restore_while_running(
container.status = "running"
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
install_addon_ssh.path_data.mkdir()
install_addon_ssh.path_config.mkdir()
await install_addon_ssh.load()

tarfile = SecureTarFile(get_fixture_path("backup_local_ssh_stopped.tar.gz"), "r")
Expand All @@ -610,7 +604,6 @@ async def test_restore_while_running_with_watchdog(
container.status = "running"
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
install_addon_ssh.path_data.mkdir()
install_addon_ssh.path_config.mkdir()
install_addon_ssh.watchdog = True
await install_addon_ssh.load()

Expand Down
49 changes: 24 additions & 25 deletions tests/backups/test_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,6 @@ async def test_backup_with_healthcheck(
container.status = "running"
container.attrs["Config"] = {"Healthcheck": "exists"}
install_addon_ssh.path_data.mkdir()
install_addon_ssh.path_config.mkdir()
coresys.core.state = CoreState.RUNNING
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
await install_addon_ssh.load()
Expand Down Expand Up @@ -827,7 +826,6 @@ async def test_restore_with_healthcheck(
container.status = "running"
container.attrs["Config"] = {"Healthcheck": "exists"}
install_addon_ssh.path_data.mkdir()
install_addon_ssh.path_config.mkdir()
coresys.core.state = CoreState.RUNNING
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
await install_addon_ssh.load()
Expand Down Expand Up @@ -922,7 +920,6 @@ async def test_backup_progress(
"""Test progress is tracked during backups."""
container.status = "running"
install_addon_ssh.path_data.mkdir()
install_addon_ssh.path_config.mkdir()
coresys.core.state = CoreState.RUNNING
coresys.hardware.disk.get_disk_free_space = lambda x: 5000

Expand Down Expand Up @@ -1024,7 +1021,6 @@ async def test_restore_progress(
"""Test progress is tracked during backups."""
container.status = "running"
install_addon_ssh.path_data.mkdir()
install_addon_ssh.path_config.mkdir()
install_addon_ssh.state = AddonState.STARTED
coresys.core.state = CoreState.RUNNING
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
Expand Down Expand Up @@ -1324,7 +1320,6 @@ async def test_restore_only_reloads_ingress_on_change(
):
"""Test restore only tells core to reload ingress when something has changed."""
install_addon_ssh.path_data.mkdir()
install_addon_ssh.path_config.mkdir()
coresys.core.state = CoreState.RUNNING
coresys.hardware.disk.get_disk_free_space = lambda x: 5000

Expand Down Expand Up @@ -1380,7 +1375,7 @@ async def mock_is_running(*_) -> bool:

async def test_restore_new_addon(
coresys: CoreSys,
install_addon_ssh: Addon,
install_addon_example: Addon,
container: MagicMock,
tmp_supervisor_data,
path_extern,
Expand All @@ -1389,26 +1384,28 @@ async def test_restore_new_addon(
coresys.core.state = CoreState.RUNNING
coresys.hardware.disk.get_disk_free_space = lambda x: 5000

assert not install_addon_ssh.path_data.exists()
assert not install_addon_ssh.path_config.exists()
assert not install_addon_example.path_data.exists()
assert not install_addon_example.path_config.exists()

backup: Backup = await coresys.backups.do_backup_partial(addons=["local_ssh"])
await coresys.addons.uninstall("local_ssh")
assert "local_ssh" not in coresys.addons.local
backup: Backup = await coresys.backups.do_backup_partial(addons=["local_example"])
await coresys.addons.uninstall("local_example")
assert "local_example" not in coresys.addons.local

with patch.object(AddonModel, "_validate_availability"), patch.object(
DockerAddon, "attach"
):
assert await coresys.backups.do_restore_partial(backup, addons=["local_ssh"])
assert await coresys.backups.do_restore_partial(
backup, addons=["local_example"]
)

assert "local_ssh" in coresys.addons.local
assert install_addon_ssh.path_data.exists()
assert install_addon_ssh.path_config.exists()
assert "local_example" in coresys.addons.local
assert install_addon_example.path_data.exists()
assert install_addon_example.path_config.exists()


async def test_restore_preserves_data_config(
coresys: CoreSys,
install_addon_ssh: Addon,
install_addon_example: Addon,
container: MagicMock,
tmp_supervisor_data,
path_extern,
Expand All @@ -1417,20 +1414,22 @@ async def test_restore_preserves_data_config(
coresys.core.state = CoreState.RUNNING
coresys.hardware.disk.get_disk_free_space = lambda x: 5000

install_addon_ssh.path_data.mkdir()
(test_data := install_addon_ssh.path_data / "data.txt").touch()
install_addon_ssh.path_config.mkdir()
(test_config := install_addon_ssh.path_config / "config.yaml").touch()
install_addon_example.path_data.mkdir()
(test_data := install_addon_example.path_data / "data.txt").touch()
install_addon_example.path_config.mkdir()
(test_config := install_addon_example.path_config / "config.yaml").touch()

backup: Backup = await coresys.backups.do_backup_partial(addons=["local_ssh"])
await coresys.addons.uninstall("local_ssh")
assert not install_addon_ssh.path_data.exists()
assert not install_addon_ssh.path_config.exists()
backup: Backup = await coresys.backups.do_backup_partial(addons=["local_example"])
await coresys.addons.uninstall("local_example")
assert not install_addon_example.path_data.exists()
assert not install_addon_example.path_config.exists()

with patch.object(AddonModel, "_validate_availability"), patch.object(
DockerAddon, "attach"
):
assert await coresys.backups.do_restore_partial(backup, addons=["local_ssh"])
assert await coresys.backups.do_restore_partial(
backup, addons=["local_example"]
)

assert test_data.exists()
assert test_config.exists()
Expand Down
Loading

0 comments on commit 88fa7ef

Please sign in to comment.