Extract instance ID helper from updater (#35043)

This commit is contained in:
Paulus Schoutsen 2020-05-04 11:23:12 -07:00 committed by GitHub
parent b3e637ca7a
commit e54e9279e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 72 additions and 37 deletions

View file

@ -1,9 +1,7 @@
"""Support to check for available updates.""" """Support to check for available updates."""
from datetime import timedelta from datetime import timedelta
from distutils.version import StrictVersion from distutils.version import StrictVersion
import json
import logging import logging
import uuid
import async_timeout import async_timeout
from distro import linux_distribution # pylint: disable=import-error from distro import linux_distribution # pylint: disable=import-error
@ -25,7 +23,6 @@ CONF_COMPONENT_REPORTING = "include_used_components"
DOMAIN = "updater" DOMAIN = "updater"
UPDATER_URL = "https://updater.home-assistant.io/" UPDATER_URL = "https://updater.home-assistant.io/"
UPDATER_UUID_FILE = ".uuid"
CONFIG_SCHEMA = vol.Schema( CONFIG_SCHEMA = vol.Schema(
{ {
@ -52,26 +49,6 @@ class Updater:
self.newest_version = newest_version self.newest_version = newest_version
def _create_uuid(hass, filename=UPDATER_UUID_FILE):
"""Create UUID and save it in a file."""
with open(hass.config.path(filename), "w") as fptr:
_uuid = uuid.uuid4().hex
fptr.write(json.dumps({"uuid": _uuid}))
return _uuid
def _load_uuid(hass, filename=UPDATER_UUID_FILE):
"""Load UUID from a file or return None."""
try:
with open(hass.config.path(filename)) as fptr:
jsonf = json.loads(fptr.read())
return uuid.UUID(jsonf["uuid"], version=4).hex
except (ValueError, AttributeError):
return None
except FileNotFoundError:
return _create_uuid(hass, filename)
async def async_setup(hass, config): async def async_setup(hass, config):
"""Set up the updater component.""" """Set up the updater component."""
if "dev" in current_version: if "dev" in current_version:
@ -80,7 +57,7 @@ async def async_setup(hass, config):
conf = config.get(DOMAIN, {}) conf = config.get(DOMAIN, {})
if conf.get(CONF_REPORTING): if conf.get(CONF_REPORTING):
huuid = await hass.async_add_job(_load_uuid, hass) huuid = await hass.helpers.instance_id.async_get()
else: else:
huuid = None huuid = None

View file

@ -7,7 +7,6 @@ import uuid
import attr import attr
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.loader import bind_hass
from .singleton import singleton from .singleton import singleton
from .typing import HomeAssistantType from .typing import HomeAssistantType
@ -367,7 +366,6 @@ class DeviceRegistry:
self._async_update_device(dev_id, area_id=None) self._async_update_device(dev_id, area_id=None)
@bind_hass
@singleton(DATA_REGISTRY) @singleton(DATA_REGISTRY)
async def async_get_registry(hass: HomeAssistantType) -> DeviceRegistry: async def async_get_registry(hass: HomeAssistantType) -> DeviceRegistry:
"""Create entity registry.""" """Create entity registry."""

View file

@ -34,7 +34,6 @@ from homeassistant.const import (
) )
from homeassistant.core import Event, callback, split_entity_id, valid_entity_id from homeassistant.core import Event, callback, split_entity_id, valid_entity_id
from homeassistant.helpers.device_registry import EVENT_DEVICE_REGISTRY_UPDATED from homeassistant.helpers.device_registry import EVENT_DEVICE_REGISTRY_UPDATED
from homeassistant.loader import bind_hass
from homeassistant.util import slugify from homeassistant.util import slugify
from homeassistant.util.yaml import load_yaml from homeassistant.util.yaml import load_yaml
@ -491,7 +490,6 @@ class EntityRegistry:
self.async_remove(entity_id) self.async_remove(entity_id)
@bind_hass
@singleton(DATA_REGISTRY) @singleton(DATA_REGISTRY)
async def async_get_registry(hass: HomeAssistantType) -> EntityRegistry: async def async_get_registry(hass: HomeAssistantType) -> EntityRegistry:
"""Create entity registry.""" """Create entity registry."""

View file

@ -0,0 +1,31 @@
"""Helper to create a unique instance ID."""
from typing import Dict, Optional
import uuid
from homeassistant.core import HomeAssistant
from . import singleton, storage
DATA_KEY = "core.uuid"
DATA_VERSION = 1
LEGACY_UUID_FILE = ".uuid"
@singleton.singleton(DATA_KEY)
async def async_get(hass: HomeAssistant) -> str:
"""Get unique ID for the hass instance."""
store = storage.Store(hass, DATA_VERSION, DATA_KEY, True)
data: Optional[Dict[str, str]] = await storage.async_migrator( # type: ignore
hass, hass.config.path(LEGACY_UUID_FILE), store,
)
if data is not None:
return data["uuid"]
data = {"uuid": uuid.uuid4().hex}
await store.async_save(data)
return data["uuid"]

View file

@ -4,6 +4,7 @@ import functools
from typing import Awaitable, Callable, TypeVar, cast from typing import Awaitable, Callable, TypeVar, cast
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.loader import bind_hass
T = TypeVar("T") T = TypeVar("T")
@ -19,6 +20,7 @@ def singleton(data_key: str) -> Callable[[FUNC], FUNC]:
def wrapper(func: FUNC) -> FUNC: def wrapper(func: FUNC) -> FUNC:
"""Wrap a function with caching logic.""" """Wrap a function with caching logic."""
@bind_hass
@functools.wraps(func) @functools.wraps(func)
async def wrapped(hass: HomeAssistant) -> T: async def wrapped(hass: HomeAssistant) -> T:
obj_or_evt = hass.data.get(data_key) obj_or_evt = hass.data.get(data_key)

View file

@ -20,29 +20,32 @@ _LOGGER = logging.getLogger(__name__)
@bind_hass @bind_hass
async def async_migrator( async def async_migrator(
hass, hass, old_path, store, *, old_conf_load_func=None, old_conf_migrate_func=None,
old_path,
store,
*,
old_conf_load_func=json_util.load_json,
old_conf_migrate_func=None,
): ):
"""Migrate old data to a store and then load data. """Migrate old data to a store and then load data.
async def old_conf_migrate_func(old_data) async def old_conf_migrate_func(old_data)
""" """
store_data = await store.async_load()
# If we already have store data we have already migrated in the past.
if store_data is not None:
return store_data
def load_old_config(): def load_old_config():
"""Load old config.""" """Load old config."""
if not os.path.isfile(old_path): if not os.path.isfile(old_path):
return None return None
return old_conf_load_func(old_path) if old_conf_load_func is not None:
return old_conf_load_func(old_path)
return json_util.load_json(old_path)
config = await hass.async_add_executor_job(load_old_config) config = await hass.async_add_executor_job(load_old_config)
if config is None: if config is None:
return await store.async_load() return None
if old_conf_migrate_func is not None: if old_conf_migrate_func is not None:
config = await old_conf_migrate_func(config) config = await old_conf_migrate_func(config)

View file

@ -37,7 +37,7 @@ def mock_get_newest_version_fixture():
@pytest.fixture(name="mock_get_uuid", autouse=True) @pytest.fixture(name="mock_get_uuid", autouse=True)
def mock_get_uuid_fixture(): def mock_get_uuid_fixture():
"""Fixture to mock get_uuid.""" """Fixture to mock get_uuid."""
with patch("homeassistant.components.updater._load_uuid") as mock: with patch("homeassistant.helpers.instance_id.async_get") as mock:
yield mock yield mock

View file

@ -0,0 +1,26 @@
"""Tests for instance ID helper."""
from tests.async_mock import patch
async def test_get_id_empty(hass, hass_storage):
"""Get unique ID."""
uuid = await hass.helpers.instance_id.async_get()
assert uuid is not None
# Assert it's stored
assert hass_storage["core.uuid"]["data"]["uuid"] == uuid
async def test_get_id_migrate(hass, hass_storage):
"""Migrate existing file."""
with patch(
"homeassistant.util.json.load_json", return_value={"uuid": "1234"}
), patch("os.path.isfile", return_value=True), patch("os.remove") as mock_remove:
uuid = await hass.helpers.instance_id.async_get()
assert uuid == "1234"
# Assert it's stored
assert hass_storage["core.uuid"]["data"]["uuid"] == uuid
# assert old deleted
assert len(mock_remove.mock_calls) == 1