Allow removing addon config on uninstall (#4913)

This commit is contained in:
Mike Degatano 2024-02-29 10:24:51 -05:00 committed by GitHub
parent 8b5c808e8c
commit 5126820619
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 63 additions and 7 deletions

View File

@ -687,7 +687,7 @@ class Addon(AddonModel):
limit=JobExecutionLimit.GROUP_ONCE,
on_condition=AddonsJobError,
)
async def uninstall(self) -> None:
async def uninstall(self, *, remove_config: bool) -> None:
"""Uninstall and cleanup this addon."""
try:
await self.instance.remove()
@ -698,6 +698,10 @@ class Addon(AddonModel):
await self.unload()
# Remove config if present and requested
if self.addon_config_used and remove_config:
await remove_data(self.path_config)
# Cleanup audio settings
if self.path_pulse.exists():
with suppress(OSError):

View File

@ -173,13 +173,13 @@ class AddonManager(CoreSysAttributes):
_LOGGER.info("Add-on '%s' successfully installed", slug)
async def uninstall(self, slug: str) -> None:
async def uninstall(self, slug: str, *, remove_config: bool = False) -> None:
"""Remove an add-on."""
if slug not in self.local:
_LOGGER.warning("Add-on %s is not installed", slug)
return
await self.local[slug].uninstall()
await self.local[slug].uninstall(remove_config=remove_config)
_LOGGER.info("Add-on '%s' successfully removed", slug)

View File

@ -106,7 +106,7 @@ from ..exceptions import (
PwnedSecret,
)
from ..validate import docker_ports
from .const import ATTR_SIGNED, CONTENT_TYPE_BINARY
from .const import ATTR_REMOVE_CONFIG, ATTR_SIGNED, CONTENT_TYPE_BINARY
from .utils import api_process, api_process_raw, api_validate, json_loads
_LOGGER: logging.Logger = logging.getLogger(__name__)
@ -126,9 +126,13 @@ SCHEMA_OPTIONS = vol.Schema(
}
)
# pylint: disable=no-value-for-parameter
SCHEMA_SECURITY = vol.Schema({vol.Optional(ATTR_PROTECTED): vol.Boolean()})
SCHEMA_UNINSTALL = vol.Schema(
{vol.Optional(ATTR_REMOVE_CONFIG, default=False): vol.Boolean()}
)
# pylint: enable=no-value-for-parameter
class APIAddons(CoreSysAttributes):
"""Handle RESTful API for add-on functions."""
@ -385,10 +389,15 @@ class APIAddons(CoreSysAttributes):
}
@api_process
def uninstall(self, request: web.Request) -> Awaitable[None]:
async def uninstall(self, request: web.Request) -> Awaitable[None]:
"""Uninstall add-on."""
addon = self._extract_addon(request)
return asyncio.shield(self.sys_addons.uninstall(addon.slug))
body: dict[str, Any] = await api_validate(SCHEMA_UNINSTALL, request)
return await asyncio.shield(
self.sys_addons.uninstall(
addon.slug, remove_config=body[ATTR_REMOVE_CONFIG]
)
)
@api_process
async def start(self, request: web.Request) -> None:

View File

@ -46,6 +46,7 @@ ATTR_MOUNTS = "mounts"
ATTR_MOUNT_POINTS = "mount_points"
ATTR_PANEL_PATH = "panel_path"
ATTR_REMOVABLE = "removable"
ATTR_REMOVE_CONFIG = "remove_config"
ATTR_REVISION = "revision"
ATTR_SEAT = "seat"
ATTR_SIGNED = "signed"

View File

@ -220,3 +220,45 @@ async def test_api_addon_rebuild_healthcheck(
assert state_changes == [AddonState.STOPPED, AddonState.STARTUP]
assert install_addon_ssh.state == AddonState.STARTED
assert resp.status == 200
async def test_api_addon_uninstall(
api_client: TestClient,
coresys: CoreSys,
install_addon_example: Addon,
tmp_supervisor_data,
path_extern,
):
"""Test uninstall."""
install_addon_example.data["map"].append(
{"type": "addon_config", "read_only": False}
)
install_addon_example.path_config.mkdir()
(test_file := install_addon_example.path_config / "test.txt").touch()
resp = await api_client.post("/addons/local_example/uninstall")
assert resp.status == 200
assert not coresys.addons.get("local_example", local_only=True)
assert test_file.exists()
async def test_api_addon_uninstall_remove_config(
api_client: TestClient,
coresys: CoreSys,
install_addon_example: Addon,
tmp_supervisor_data,
path_extern,
):
"""Test uninstall and remove config."""
install_addon_example.data["map"].append(
{"type": "addon_config", "read_only": False}
)
(test_folder := install_addon_example.path_config).mkdir()
(install_addon_example.path_config / "test.txt").touch()
resp = await api_client.post(
"/addons/local_example/uninstall", json={"remove_config": True}
)
assert resp.status == 200
assert not coresys.addons.get("local_example", local_only=True)
assert not test_folder.exists()