mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-25 18:16:32 +00:00
Allow removing addon config on uninstall (#4913)
This commit is contained in:
parent
8b5c808e8c
commit
5126820619
@ -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):
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user