mirror of
https://github.com/home-assistant/supervisor.git
synced 2025-10-30 14:09:47 +00:00
When receiving a shutdown signal during startup, the Supervisor should cancel its startup task to ensure a graceful shutdown. This prevents Supervisor accidentally accessing the Event loop after it has been closed by the stop procedure: RuntimeError: Event loop stopped before Future completed.
100 lines
3.0 KiB
Python
100 lines
3.0 KiB
Python
"""Main file for Supervisor."""
|
|
|
|
import asyncio
|
|
from concurrent.futures import ThreadPoolExecutor
|
|
import logging
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
import zlib_fast
|
|
|
|
# Enable fast zlib before importing supervisor
|
|
zlib_fast.enable()
|
|
|
|
# pylint: disable=wrong-import-position
|
|
from supervisor import bootstrap # noqa: E402
|
|
from supervisor.utils.blockbuster import BlockBusterManager # noqa: E402
|
|
from supervisor.utils.logging import activate_log_queue_handler # noqa: E402
|
|
|
|
# pylint: enable=wrong-import-position
|
|
|
|
_LOGGER: logging.Logger = logging.getLogger(__name__)
|
|
|
|
CONTAINER_OS_STARTUP_CHECK = Path("/run/os/startup-marker")
|
|
|
|
|
|
def run_os_startup_check_cleanup() -> None:
|
|
"""Cleanup OS startup check."""
|
|
if not CONTAINER_OS_STARTUP_CHECK.exists():
|
|
return
|
|
|
|
try:
|
|
CONTAINER_OS_STARTUP_CHECK.unlink()
|
|
except OSError as err:
|
|
_LOGGER.warning("Not able to remove the startup health file: %s", err)
|
|
|
|
|
|
# pylint: disable=invalid-name
|
|
if __name__ == "__main__":
|
|
bootstrap.initialize_logging()
|
|
|
|
# Init async event loop
|
|
loop = asyncio.new_event_loop()
|
|
asyncio.set_event_loop(loop)
|
|
|
|
# Check if all information are available to setup Supervisor
|
|
bootstrap.check_environment()
|
|
|
|
# init executor pool
|
|
executor = ThreadPoolExecutor(thread_name_prefix="SyncWorker")
|
|
loop.set_default_executor(executor)
|
|
|
|
activate_log_queue_handler()
|
|
|
|
_LOGGER.info("Initializing Supervisor setup")
|
|
coresys = loop.run_until_complete(bootstrap.initialize_coresys())
|
|
loop.set_debug(coresys.config.debug)
|
|
if coresys.config.detect_blocking_io:
|
|
BlockBusterManager.activate()
|
|
loop.run_until_complete(coresys.core.connect())
|
|
|
|
loop.run_until_complete(bootstrap.supervisor_debugger(coresys))
|
|
|
|
# Signal health startup for container
|
|
run_os_startup_check_cleanup()
|
|
|
|
_LOGGER.info("Setting up Supervisor")
|
|
loop.run_until_complete(coresys.core.setup())
|
|
|
|
# Create startup task that can be cancelled gracefully
|
|
startup_task = loop.create_task(coresys.core.start())
|
|
|
|
def shutdown_handler() -> None:
|
|
"""Handle shutdown signals gracefully during startup."""
|
|
if not startup_task.done():
|
|
_LOGGER.warning("Supervisor startup interrupted by shutdown signal")
|
|
startup_task.cancel()
|
|
|
|
coresys.create_task(coresys.core.stop())
|
|
|
|
bootstrap.register_signal_handlers(loop, shutdown_handler)
|
|
|
|
try:
|
|
loop.run_until_complete(startup_task)
|
|
except asyncio.CancelledError:
|
|
_LOGGER.warning("Supervisor startup cancelled")
|
|
except Exception as err: # pylint: disable=broad-except
|
|
# Supervisor itself is running at this point, just something didn't
|
|
# start as expected. Log with traceback to get more insights for
|
|
# such cases.
|
|
_LOGGER.critical("Supervisor start failed: %s", err, exc_info=True)
|
|
|
|
try:
|
|
_LOGGER.info("Running Supervisor")
|
|
loop.run_forever()
|
|
finally:
|
|
loop.close()
|
|
|
|
_LOGGER.info("Closing Supervisor")
|
|
sys.exit(coresys.core.exit_code)
|