Add repair for add-on boot fail (#129847)
parent
8a20cd77a0
commit
383f712d43
|
@ -103,6 +103,7 @@ PLACEHOLDER_KEY_ADDON_URL = "addon_url"
|
|||
PLACEHOLDER_KEY_REFERENCE = "reference"
|
||||
PLACEHOLDER_KEY_COMPONENTS = "components"
|
||||
|
||||
ISSUE_KEY_ADDON_BOOT_FAIL = "issue_addon_boot_fail"
|
||||
ISSUE_KEY_SYSTEM_DOCKER_CONFIG = "issue_system_docker_config"
|
||||
ISSUE_KEY_ADDON_DETACHED_ADDON_MISSING = "issue_addon_detached_addon_missing"
|
||||
ISSUE_KEY_ADDON_DETACHED_ADDON_REMOVED = "issue_addon_detached_addon_removed"
|
||||
|
|
|
@ -36,6 +36,7 @@ from .const import (
|
|||
EVENT_SUPERVISOR_EVENT,
|
||||
EVENT_SUPERVISOR_UPDATE,
|
||||
EVENT_SUPPORTED_CHANGED,
|
||||
ISSUE_KEY_ADDON_BOOT_FAIL,
|
||||
ISSUE_KEY_ADDON_DETACHED_ADDON_MISSING,
|
||||
ISSUE_KEY_ADDON_DETACHED_ADDON_REMOVED,
|
||||
ISSUE_KEY_SYSTEM_DOCKER_CONFIG,
|
||||
|
@ -94,6 +95,7 @@ UNHEALTHY_REASONS = {
|
|||
|
||||
# Keys (type + context) of issues that when found should be made into a repair
|
||||
ISSUE_KEYS_FOR_REPAIRS = {
|
||||
ISSUE_KEY_ADDON_BOOT_FAIL,
|
||||
"issue_mount_mount_failed",
|
||||
"issue_system_multiple_data_disks",
|
||||
"issue_system_reboot_required",
|
||||
|
|
|
@ -14,6 +14,7 @@ from homeassistant.data_entry_flow import FlowResult
|
|||
|
||||
from . import get_addons_info, get_issues_info
|
||||
from .const import (
|
||||
ISSUE_KEY_ADDON_BOOT_FAIL,
|
||||
ISSUE_KEY_ADDON_DETACHED_ADDON_REMOVED,
|
||||
ISSUE_KEY_SYSTEM_DOCKER_CONFIG,
|
||||
PLACEHOLDER_KEY_ADDON,
|
||||
|
@ -181,8 +182,8 @@ class DockerConfigIssueRepairFlow(SupervisorIssueRepairFlow):
|
|||
return placeholders
|
||||
|
||||
|
||||
class DetachedAddonIssueRepairFlow(SupervisorIssueRepairFlow):
|
||||
"""Handler for detached addon issue fixing flows."""
|
||||
class AddonIssueRepairFlow(SupervisorIssueRepairFlow):
|
||||
"""Handler for addon issue fixing flows."""
|
||||
|
||||
@property
|
||||
def description_placeholders(self) -> dict[str, str] | None:
|
||||
|
@ -210,7 +211,10 @@ async def async_create_fix_flow(
|
|||
issue = supervisor_issues and supervisor_issues.get_issue(issue_id)
|
||||
if issue and issue.key == ISSUE_KEY_SYSTEM_DOCKER_CONFIG:
|
||||
return DockerConfigIssueRepairFlow(issue_id)
|
||||
if issue and issue.key == ISSUE_KEY_ADDON_DETACHED_ADDON_REMOVED:
|
||||
return DetachedAddonIssueRepairFlow(issue_id)
|
||||
if issue and issue.key in {
|
||||
ISSUE_KEY_ADDON_DETACHED_ADDON_REMOVED,
|
||||
ISSUE_KEY_ADDON_BOOT_FAIL,
|
||||
}:
|
||||
return AddonIssueRepairFlow(issue_id)
|
||||
|
||||
return SupervisorIssueRepairFlow(issue_id)
|
||||
|
|
|
@ -17,6 +17,23 @@
|
|||
}
|
||||
},
|
||||
"issues": {
|
||||
"issue_addon_boot_fail": {
|
||||
"title": "Add-on failed to start at boot",
|
||||
"fix_flow": {
|
||||
"step": {
|
||||
"fix_menu": {
|
||||
"description": "Add-on {addon} is set to start at boot but failed to start. Usually this occurs when the configuration is incorrect or the same port is used in multiple add-ons. Check the configuration as well as logs for {addon} and Supervisor.\n\nUse Start to try again or Disable to turn off the start at boot option.",
|
||||
"menu_options": {
|
||||
"addon_execute_start": "Start",
|
||||
"addon_disable_boot": "Disable"
|
||||
}
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"apply_suggestion_fail": "Could not apply the fix. Check the Supervisor logs for more details."
|
||||
}
|
||||
}
|
||||
},
|
||||
"issue_addon_detached_addon_missing": {
|
||||
"title": "Missing repository for an installed add-on",
|
||||
"description": "Repository for add-on {addon} is missing. This means it will not get updates, and backups may not be restored correctly as the supervisor may not be able to build/download the resources required.\n\nPlease check the [add-on's documentation]({addon_url}) for installation instructions and add the repository to the store."
|
||||
|
|
|
@ -868,3 +868,104 @@ async def test_supervisor_issue_detached_addon_removed(
|
|||
str(aioclient_mock.mock_calls[-1][1])
|
||||
== "http://127.0.0.1/resolution/suggestion/1235"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"all_setup_requests", [{"include_addons": True}], indirect=True
|
||||
)
|
||||
@pytest.mark.usefixtures("all_setup_requests")
|
||||
async def test_supervisor_issue_addon_boot_fail(
|
||||
hass: HomeAssistant,
|
||||
aioclient_mock: AiohttpClientMocker,
|
||||
hass_client: ClientSessionGenerator,
|
||||
issue_registry: ir.IssueRegistry,
|
||||
) -> None:
|
||||
"""Test fix flow for supervisor issue."""
|
||||
mock_resolution_info(
|
||||
aioclient_mock,
|
||||
issues=[
|
||||
{
|
||||
"uuid": "1234",
|
||||
"type": "boot_fail",
|
||||
"context": "addon",
|
||||
"reference": "test",
|
||||
"suggestions": [
|
||||
{
|
||||
"uuid": "1235",
|
||||
"type": "execute_start",
|
||||
"context": "addon",
|
||||
"reference": "test",
|
||||
},
|
||||
{
|
||||
"uuid": "1236",
|
||||
"type": "disable_boot",
|
||||
"context": "addon",
|
||||
"reference": "test",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
assert await async_setup_component(hass, "hassio", {})
|
||||
|
||||
repair_issue = issue_registry.async_get_issue(domain="hassio", issue_id="1234")
|
||||
assert repair_issue
|
||||
|
||||
client = await hass_client()
|
||||
|
||||
resp = await client.post(
|
||||
"/api/repairs/issues/fix",
|
||||
json={"handler": "hassio", "issue_id": repair_issue.issue_id},
|
||||
)
|
||||
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
|
||||
flow_id = data["flow_id"]
|
||||
assert data == {
|
||||
"type": "menu",
|
||||
"flow_id": flow_id,
|
||||
"handler": "hassio",
|
||||
"step_id": "fix_menu",
|
||||
"data_schema": [
|
||||
{
|
||||
"type": "select",
|
||||
"options": [
|
||||
["addon_execute_start", "addon_execute_start"],
|
||||
["addon_disable_boot", "addon_disable_boot"],
|
||||
],
|
||||
"name": "next_step_id",
|
||||
}
|
||||
],
|
||||
"menu_options": ["addon_execute_start", "addon_disable_boot"],
|
||||
"description_placeholders": {
|
||||
"reference": "test",
|
||||
"addon": "test",
|
||||
},
|
||||
}
|
||||
|
||||
resp = await client.post(
|
||||
f"/api/repairs/issues/fix/{flow_id}",
|
||||
json={"next_step_id": "addon_execute_start"},
|
||||
)
|
||||
|
||||
assert resp.status == HTTPStatus.OK
|
||||
data = await resp.json()
|
||||
|
||||
flow_id = data["flow_id"]
|
||||
assert data == {
|
||||
"type": "create_entry",
|
||||
"flow_id": flow_id,
|
||||
"handler": "hassio",
|
||||
"description": None,
|
||||
"description_placeholders": None,
|
||||
}
|
||||
|
||||
assert not issue_registry.async_get_issue(domain="hassio", issue_id="1234")
|
||||
|
||||
assert aioclient_mock.mock_calls[-1][0] == "post"
|
||||
assert (
|
||||
str(aioclient_mock.mock_calls[-1][1])
|
||||
== "http://127.0.0.1/resolution/suggestion/1235"
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue