Fix setup race when config entry is in a setup retry state (#73145)

This commit is contained in:
J. Nick Koston 2022-06-06 19:48:49 -10:00 committed by GitHub
parent 0c21bf7c25
commit 2e47cee72a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 77 additions and 1 deletions

View file

@ -90,6 +90,8 @@ class ConfigEntryState(Enum):
"""The config entry has not been loaded"""
FAILED_UNLOAD = "failed_unload", False
"""An error occurred when trying to unload the entry"""
SETUP_IN_PROGRESS = "setup_in_progress", True
"""The config entry is setting up."""
_recoverable: bool
@ -294,6 +296,10 @@ class ConfigEntry:
if integration is None:
integration = await loader.async_get_integration(hass, self.domain)
# Only store setup result as state if it was not forwarded.
if self.domain == integration.domain:
self.state = ConfigEntryState.SETUP_IN_PROGRESS
self.supports_unload = await support_entry_unload(hass, self.domain)
self.supports_remove_device = await support_remove_from_device(
hass, self.domain

View file

@ -16,7 +16,7 @@ from homeassistant.const import (
EVENT_HOMEASSISTANT_STARTED,
EVENT_HOMEASSISTANT_STOP,
)
from homeassistant.core import CoreState, Event, callback
from homeassistant.core import CoreState, Event, HomeAssistant, callback
from homeassistant.data_entry_flow import RESULT_TYPE_ABORT, BaseServiceInfo, FlowResult
from homeassistant.exceptions import (
ConfigEntryAuthFailed,
@ -3190,3 +3190,73 @@ async def test_entry_reload_concurrency(hass, manager):
await asyncio.gather(*tasks)
assert entry.state is config_entries.ConfigEntryState.LOADED
assert loaded == 1
async def test_unique_id_update_while_setup_in_progress(
hass: HomeAssistant, manager: config_entries.ConfigEntries
) -> None:
"""Test we handle the case where the config entry is updated while setup is in progress."""
async def mock_setup_entry(hass, entry):
"""Mock setting up entry."""
await asyncio.sleep(0.1)
return True
async def mock_unload_entry(hass, entry):
"""Mock unloading an entry."""
return True
hass.config.components.add("comp")
entry = MockConfigEntry(
domain="comp",
data={"additional": "data", "host": "0.0.0.0"},
unique_id="mock-unique-id",
state=config_entries.ConfigEntryState.SETUP_RETRY,
)
entry.add_to_hass(hass)
mock_integration(
hass,
MockModule(
"comp",
async_setup_entry=mock_setup_entry,
async_unload_entry=mock_unload_entry,
),
)
mock_entity_platform(hass, "config_flow.comp", None)
updates = {"host": "1.1.1.1"}
hass.async_create_task(hass.config_entries.async_reload(entry.entry_id))
await asyncio.sleep(0)
assert entry.state is config_entries.ConfigEntryState.SETUP_IN_PROGRESS
class TestFlow(config_entries.ConfigFlow):
"""Test flow."""
VERSION = 1
async def async_step_user(self, user_input=None):
"""Test user step."""
await self.async_set_unique_id("mock-unique-id")
await self._abort_if_unique_id_configured(
updates=updates, reload_on_update=True
)
with patch.dict(config_entries.HANDLERS, {"comp": TestFlow}), patch(
"homeassistant.config_entries.ConfigEntries.async_reload"
) as async_reload:
result = await manager.flow.async_init(
"comp", context={"source": config_entries.SOURCE_USER}
)
await hass.async_block_till_done()
assert result["type"] == RESULT_TYPE_ABORT
assert result["reason"] == "already_configured"
assert entry.data["host"] == "1.1.1.1"
assert entry.data["additional"] == "data"
# Setup is already in progress, we should not reload
# if it fails it will go into a retry state and try again
assert len(async_reload.mock_calls) == 0
await hass.async_block_till_done()
assert entry.state is config_entries.ConfigEntryState.LOADED