mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-07-08 17:56:33 +00:00
Add manual_only
option to addon boot config (#5272)
* Add manual_forced option to addon boot config * Include client library in pull request template * Add boot_config to api output so frontend can use it * `manual_forced` to `manual_only`
This commit is contained in:
parent
91a8fae9b5
commit
0177cd9528
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
7
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -38,6 +38,7 @@
|
||||
- This PR is related to issue:
|
||||
- Link to documentation pull request:
|
||||
- Link to cli pull request:
|
||||
- Link to client library pull request:
|
||||
|
||||
## Checklist
|
||||
|
||||
@ -55,9 +56,11 @@
|
||||
- [ ] The code has been formatted using Ruff (`ruff format supervisor tests`)
|
||||
- [ ] Tests have been added to verify that the new code works.
|
||||
|
||||
If API endpoints of add-on configuration are added/changed:
|
||||
If API endpoints or add-on configuration are added/changed:
|
||||
|
||||
- [ ] Documentation added/updated for [developers.home-assistant.io][docs-repository]
|
||||
- [ ] [CLI][cli-repository] updated (if necessary)
|
||||
- [ ] [Client library][client-library-repository] updated (if necessary)
|
||||
|
||||
<!--
|
||||
Thank you for contributing <3
|
||||
@ -67,3 +70,5 @@ If API endpoints of add-on configuration are added/changed:
|
||||
|
||||
[dev-checklist]: https://developers.home-assistant.io/docs/en/development_checklist.html
|
||||
[docs-repository]: https://github.com/home-assistant/developers.home-assistant
|
||||
[cli-repository]: https://github.com/home-assistant/cli
|
||||
[client-library-repository]: https://github.com/home-assistant-libs/python-supervisor-client/
|
||||
|
@ -57,6 +57,7 @@ from ..const import (
|
||||
ATTR_WATCHDOG,
|
||||
DNS_SUFFIX,
|
||||
AddonBoot,
|
||||
AddonBootConfig,
|
||||
AddonStartup,
|
||||
AddonState,
|
||||
BusEvent,
|
||||
@ -311,7 +312,9 @@ class Addon(AddonModel):
|
||||
|
||||
@property
|
||||
def boot(self) -> AddonBoot:
|
||||
"""Return boot config with prio local settings."""
|
||||
"""Return boot config with prio local settings unless config is forced."""
|
||||
if self.boot_config == AddonBootConfig.MANUAL_ONLY:
|
||||
return super().boot
|
||||
return self.persist.get(ATTR_BOOT, super().boot)
|
||||
|
||||
@boot.setter
|
||||
|
@ -83,6 +83,7 @@ from ..const import (
|
||||
SECURITY_DISABLE,
|
||||
SECURITY_PROFILE,
|
||||
AddonBoot,
|
||||
AddonBootConfig,
|
||||
AddonStage,
|
||||
AddonStartup,
|
||||
)
|
||||
@ -150,10 +151,15 @@ class AddonModel(JobGroup, ABC):
|
||||
return self.data[ATTR_OPTIONS]
|
||||
|
||||
@property
|
||||
def boot(self) -> AddonBoot:
|
||||
"""Return boot config with prio local settings."""
|
||||
def boot_config(self) -> AddonBootConfig:
|
||||
"""Return boot config."""
|
||||
return self.data[ATTR_BOOT]
|
||||
|
||||
@property
|
||||
def boot(self) -> AddonBoot:
|
||||
"""Return boot config with prio local settings unless config is forced."""
|
||||
return AddonBoot(self.data[ATTR_BOOT])
|
||||
|
||||
@property
|
||||
def auto_update(self) -> bool | None:
|
||||
"""Return if auto update is enable."""
|
||||
|
@ -98,6 +98,7 @@ from ..const import (
|
||||
ROLE_ALL,
|
||||
ROLE_DEFAULT,
|
||||
AddonBoot,
|
||||
AddonBootConfig,
|
||||
AddonStage,
|
||||
AddonStartup,
|
||||
AddonState,
|
||||
@ -321,7 +322,9 @@ _SCHEMA_ADDON_CONFIG = vol.Schema(
|
||||
vol.Optional(ATTR_STARTUP, default=AddonStartup.APPLICATION): vol.Coerce(
|
||||
AddonStartup
|
||||
),
|
||||
vol.Optional(ATTR_BOOT, default=AddonBoot.AUTO): vol.Coerce(AddonBoot),
|
||||
vol.Optional(ATTR_BOOT, default=AddonBootConfig.AUTO): vol.Coerce(
|
||||
AddonBootConfig
|
||||
),
|
||||
vol.Optional(ATTR_INIT, default=True): vol.Boolean(),
|
||||
vol.Optional(ATTR_ADVANCED, default=False): vol.Boolean(),
|
||||
vol.Optional(ATTR_STAGE, default=AddonStage.STABLE): vol.Coerce(AddonStage),
|
||||
|
@ -98,6 +98,7 @@ from ..const import (
|
||||
ATTR_WEBUI,
|
||||
REQUEST_FROM,
|
||||
AddonBoot,
|
||||
AddonBootConfig,
|
||||
)
|
||||
from ..coresys import CoreSysAttributes
|
||||
from ..docker.stats import DockerStats
|
||||
@ -109,7 +110,7 @@ from ..exceptions import (
|
||||
PwnedSecret,
|
||||
)
|
||||
from ..validate import docker_ports
|
||||
from .const import ATTR_REMOVE_CONFIG, ATTR_SIGNED
|
||||
from .const import ATTR_BOOT_CONFIG, ATTR_REMOVE_CONFIG, ATTR_SIGNED
|
||||
from .utils import api_process, api_validate, json_loads
|
||||
|
||||
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
||||
@ -217,6 +218,7 @@ class APIAddons(CoreSysAttributes):
|
||||
ATTR_VERSION_LATEST: addon.latest_version,
|
||||
ATTR_PROTECTED: addon.protected,
|
||||
ATTR_RATING: rating_security(addon),
|
||||
ATTR_BOOT_CONFIG: addon.boot_config,
|
||||
ATTR_BOOT: addon.boot,
|
||||
ATTR_OPTIONS: addon.options,
|
||||
ATTR_SCHEMA: addon.schema_ui,
|
||||
@ -300,6 +302,10 @@ class APIAddons(CoreSysAttributes):
|
||||
if ATTR_OPTIONS in body:
|
||||
addon.options = body[ATTR_OPTIONS]
|
||||
if ATTR_BOOT in body:
|
||||
if addon.boot_config == AddonBootConfig.MANUAL_ONLY:
|
||||
raise APIError(
|
||||
f"Addon {addon.slug} boot option is set to {addon.boot_config} so it cannot be changed"
|
||||
)
|
||||
addon.boot = body[ATTR_BOOT]
|
||||
if ATTR_AUTO_UPDATE in body:
|
||||
addon.auto_update = body[ATTR_AUTO_UPDATE]
|
||||
|
@ -17,6 +17,7 @@ ATTR_APPARMOR_VERSION = "apparmor_version"
|
||||
ATTR_ATTRIBUTES = "attributes"
|
||||
ATTR_AVAILABLE_UPDATES = "available_updates"
|
||||
ATTR_BACKGROUND = "background"
|
||||
ATTR_BOOT_CONFIG = "boot_config"
|
||||
ATTR_BOOT_SLOT = "boot_slot"
|
||||
ATTR_BOOT_SLOTS = "boot_slots"
|
||||
ATTR_BOOT_TIMESTAMP = "boot_timestamp"
|
||||
|
@ -382,12 +382,27 @@ ROLE_ADMIN = "admin"
|
||||
ROLE_ALL = [ROLE_DEFAULT, ROLE_HOMEASSISTANT, ROLE_BACKUP, ROLE_MANAGER, ROLE_ADMIN]
|
||||
|
||||
|
||||
class AddonBootConfig(StrEnum):
|
||||
"""Boot mode config for the add-on."""
|
||||
|
||||
AUTO = "auto"
|
||||
MANUAL = "manual"
|
||||
MANUAL_ONLY = "manual_only"
|
||||
|
||||
|
||||
class AddonBoot(StrEnum):
|
||||
"""Boot mode for the add-on."""
|
||||
|
||||
AUTO = "auto"
|
||||
MANUAL = "manual"
|
||||
|
||||
@classmethod
|
||||
def _missing_(cls, value: str) -> Self | None:
|
||||
"""Convert 'forced' config values to their counterpart."""
|
||||
if value == AddonBootConfig.MANUAL_ONLY:
|
||||
return AddonBoot.MANUAL
|
||||
return None
|
||||
|
||||
|
||||
class AddonStartup(StrEnum):
|
||||
"""Startup types of Add-on."""
|
||||
|
@ -691,6 +691,7 @@ async def test_local_example_install(
|
||||
mock_aarch64_arch_supported: None,
|
||||
):
|
||||
"""Test install of an addon."""
|
||||
coresys.hardware.disk.get_disk_free_space = lambda x: 5000
|
||||
assert not (
|
||||
data_dir := tmp_supervisor_data / "addons" / "data" / "local_example"
|
||||
).exists()
|
||||
@ -883,3 +884,14 @@ async def test_addon_load_succeeds_with_docker_errors(
|
||||
caplog.clear()
|
||||
await install_addon_ssh.load()
|
||||
assert "Unknown error with test/amd64-addon-ssh:9.2.1" in caplog.text
|
||||
|
||||
|
||||
async def test_addon_manual_only_boot(coresys: CoreSys, install_addon_example: Addon):
|
||||
"""Test an addon with manual only boot mode."""
|
||||
assert install_addon_example.boot_config == "manual_only"
|
||||
assert install_addon_example.boot == "manual"
|
||||
|
||||
# Users cannot change boot mode of an addon with manual forced so changing boot isn't realistic
|
||||
# However boot mode can change on update and user may have set auto before, ensure it is ignored
|
||||
install_addon_example.boot = "auto"
|
||||
assert install_addon_example.boot == "manual"
|
||||
|
@ -346,3 +346,23 @@ async def test_api_addon_system_managed(
|
||||
body = await resp.json()
|
||||
assert body["data"]["system_managed"] is False
|
||||
assert body["data"]["system_managed_config_entry"] is None
|
||||
|
||||
|
||||
async def test_addon_options_boot_mode_manual_only_invalid(
|
||||
api_client: TestClient, install_addon_example: Addon
|
||||
):
|
||||
"""Test changing boot mode is invalid if set to manual only."""
|
||||
install_addon_example.data["ingress"] = False
|
||||
resp = await api_client.get("/addons/local_example/info")
|
||||
assert resp.status == 200
|
||||
body = await resp.json()
|
||||
assert body["data"]["boot"] == "manual"
|
||||
assert body["data"]["boot_config"] == "manual_only"
|
||||
|
||||
resp = await api_client.post("/addons/local_example/options", json={"boot": "auto"})
|
||||
assert resp.status == 400
|
||||
body = await resp.json()
|
||||
assert (
|
||||
body["message"]
|
||||
== "Addon local_example boot option is set to manual_only so it cannot be changed"
|
||||
)
|
||||
|
@ -23,3 +23,4 @@ ingress_port: 0
|
||||
breaking_versions:
|
||||
- test
|
||||
- 1.0
|
||||
boot: manual_only
|
||||
|
Loading…
x
Reference in New Issue
Block a user