From 81701f7e4c83e3688353133de5669fbee6b360ec Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Mon, 17 Feb 2020 10:20:05 -0800 Subject: [PATCH] Start safe mode if invalid core conf (#31904) --- homeassistant/bootstrap.py | 48 +++++++++++++++++++------------------- tests/test_bootstrap.py | 31 +++++++++++++++++++++--- 2 files changed, 52 insertions(+), 27 deletions(-) diff --git a/homeassistant/bootstrap.py b/homeassistant/bootstrap.py index 4d69698f2528..250b95eafda8 100644 --- a/homeassistant/bootstrap.py +++ b/homeassistant/bootstrap.py @@ -71,6 +71,7 @@ async def async_setup_hass( _LOGGER.info("Config directory: %s", config_dir) config_dict = None + basic_setup_success = False if not safe_mode: await hass.async_add_executor_job(conf_util.process_ha_config_upgrade, hass) @@ -86,11 +87,13 @@ async def async_setup_hass( if not is_virtual_env(): await async_mount_local_lib_path(config_dir) - await async_from_config_dict(config_dict, hass) + basic_setup_success = ( + await async_from_config_dict(config_dict, hass) is not None + ) finally: clear_secret_cache() - if safe_mode or config_dict is None: + if safe_mode or config_dict is None or not basic_setup_success: _LOGGER.info("Starting in safe mode") http_conf = (await http.async_get_last_config(hass)) or {} @@ -112,6 +115,25 @@ async def async_from_config_dict( """ start = monotonic() + hass.config_entries = config_entries.ConfigEntries(hass, config) + await hass.config_entries.async_initialize() + + # Set up core. + _LOGGER.debug("Setting up %s", CORE_INTEGRATIONS) + + if not all( + await asyncio.gather( + *( + async_setup_component(hass, domain, config) + for domain in CORE_INTEGRATIONS + ) + ) + ): + _LOGGER.error("Home Assistant core failed to initialize. ") + return None + + _LOGGER.debug("Home Assistant core initialized") + core_config = config.get(core.DOMAIN, {}) try: @@ -126,9 +148,6 @@ async def async_from_config_dict( ) return None - hass.config_entries = config_entries.ConfigEntries(hass, config) - await hass.config_entries.async_initialize() - await _async_set_up_integrations(hass, config) stop = monotonic() @@ -296,25 +315,6 @@ async def _async_set_up_integrations( return_exceptions=True, ) - # Set up core. - _LOGGER.debug("Setting up %s", CORE_INTEGRATIONS) - - if not all( - await asyncio.gather( - *( - async_setup_component(hass, domain, config) - for domain in CORE_INTEGRATIONS - ) - ) - ): - _LOGGER.error( - "Home Assistant core failed to initialize. " - "Further initialization aborted" - ) - return - - _LOGGER.debug("Home Assistant core initialized") - # Finish resolving domains for dep_domains in await resolved_domains_task: # Result is either a set or an exception. We ignore exceptions diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py index 48c5360d8886..7f653f18f0e5 100644 --- a/tests/test_bootstrap.py +++ b/tests/test_bootstrap.py @@ -50,18 +50,18 @@ async def test_load_hassio(hass): async def test_empty_setup(hass): """Test an empty set up loads the core.""" - await bootstrap._async_set_up_integrations(hass, {}) + await bootstrap.async_from_config_dict({}, hass) for domain in bootstrap.CORE_INTEGRATIONS: assert domain in hass.config.components, domain -async def test_core_failure_aborts(hass, caplog): +async def test_core_failure_loads_safe_mode(hass, caplog): """Test failing core setup aborts further setup.""" with patch( "homeassistant.components.homeassistant.async_setup", return_value=mock_coro(False), ): - await bootstrap._async_set_up_integrations(hass, {"group": {}}) + await bootstrap.async_from_config_dict({"group": {}}, hass) assert "core failed to initialize" in caplog.text # We aborted early, group not set up @@ -357,3 +357,28 @@ async def test_setup_hass_safe_mode( # Validate we didn't try to set up config entry. assert "browser" not in hass.config.components assert len(browser_setup.mock_calls) == 0 + + +async def test_setup_hass_invalid_core_config( + mock_enable_logging, + mock_is_virtual_env, + mock_mount_local_lib_path, + mock_ensure_config_exists, + mock_process_ha_config_upgrade, +): + """Test it works.""" + with patch( + "homeassistant.config.async_hass_config_yaml", + return_value={"homeassistant": {"non-existing": 1}}, + ): + hass = await bootstrap.async_setup_hass( + config_dir=get_test_config_dir(), + verbose=False, + log_rotate_days=10, + log_file="", + log_no_color=False, + skip_pip=True, + safe_mode=False, + ) + + assert "safe_mode" in hass.config.components