mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-26 18:46:29 +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,
|
limit=JobExecutionLimit.GROUP_ONCE,
|
||||||
on_condition=AddonsJobError,
|
on_condition=AddonsJobError,
|
||||||
)
|
)
|
||||||
async def uninstall(self) -> None:
|
async def uninstall(self, *, remove_config: bool) -> None:
|
||||||
"""Uninstall and cleanup this addon."""
|
"""Uninstall and cleanup this addon."""
|
||||||
try:
|
try:
|
||||||
await self.instance.remove()
|
await self.instance.remove()
|
||||||
@ -698,6 +698,10 @@ class Addon(AddonModel):
|
|||||||
|
|
||||||
await self.unload()
|
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
|
# Cleanup audio settings
|
||||||
if self.path_pulse.exists():
|
if self.path_pulse.exists():
|
||||||
with suppress(OSError):
|
with suppress(OSError):
|
||||||
|
@ -173,13 +173,13 @@ class AddonManager(CoreSysAttributes):
|
|||||||
|
|
||||||
_LOGGER.info("Add-on '%s' successfully installed", slug)
|
_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."""
|
"""Remove an add-on."""
|
||||||
if slug not in self.local:
|
if slug not in self.local:
|
||||||
_LOGGER.warning("Add-on %s is not installed", slug)
|
_LOGGER.warning("Add-on %s is not installed", slug)
|
||||||
return
|
return
|
||||||
|
|
||||||
await self.local[slug].uninstall()
|
await self.local[slug].uninstall(remove_config=remove_config)
|
||||||
|
|
||||||
_LOGGER.info("Add-on '%s' successfully removed", slug)
|
_LOGGER.info("Add-on '%s' successfully removed", slug)
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ from ..exceptions import (
|
|||||||
PwnedSecret,
|
PwnedSecret,
|
||||||
)
|
)
|
||||||
from ..validate import docker_ports
|
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
|
from .utils import api_process, api_process_raw, api_validate, json_loads
|
||||||
|
|
||||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
_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_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):
|
class APIAddons(CoreSysAttributes):
|
||||||
"""Handle RESTful API for add-on functions."""
|
"""Handle RESTful API for add-on functions."""
|
||||||
@ -385,10 +389,15 @@ class APIAddons(CoreSysAttributes):
|
|||||||
}
|
}
|
||||||
|
|
||||||
@api_process
|
@api_process
|
||||||
def uninstall(self, request: web.Request) -> Awaitable[None]:
|
async def uninstall(self, request: web.Request) -> Awaitable[None]:
|
||||||
"""Uninstall add-on."""
|
"""Uninstall add-on."""
|
||||||
addon = self._extract_addon(request)
|
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
|
@api_process
|
||||||
async def start(self, request: web.Request) -> None:
|
async def start(self, request: web.Request) -> None:
|
||||||
|
@ -46,6 +46,7 @@ ATTR_MOUNTS = "mounts"
|
|||||||
ATTR_MOUNT_POINTS = "mount_points"
|
ATTR_MOUNT_POINTS = "mount_points"
|
||||||
ATTR_PANEL_PATH = "panel_path"
|
ATTR_PANEL_PATH = "panel_path"
|
||||||
ATTR_REMOVABLE = "removable"
|
ATTR_REMOVABLE = "removable"
|
||||||
|
ATTR_REMOVE_CONFIG = "remove_config"
|
||||||
ATTR_REVISION = "revision"
|
ATTR_REVISION = "revision"
|
||||||
ATTR_SEAT = "seat"
|
ATTR_SEAT = "seat"
|
||||||
ATTR_SIGNED = "signed"
|
ATTR_SIGNED = "signed"
|
||||||
|
@ -220,3 +220,45 @@ async def test_api_addon_rebuild_healthcheck(
|
|||||||
assert state_changes == [AddonState.STOPPED, AddonState.STARTUP]
|
assert state_changes == [AddonState.STOPPED, AddonState.STARTUP]
|
||||||
assert install_addon_ssh.state == AddonState.STARTED
|
assert install_addon_ssh.state == AddonState.STARTED
|
||||||
assert resp.status == 200
|
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