Addon repositories (#2071)

* stash

* Add test

* Use executor

* Make remove a coroutine

* Change logging and return
This commit is contained in:
Joakim Sørensen 2020-09-22 23:40:36 +02:00 committed by GitHub
parent 9274a0fa17
commit 1af90721cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 76 additions and 9 deletions

View File

@ -142,6 +142,10 @@ class APISupervisor(CoreSysAttributes):
if ATTR_ADDONS_REPOSITORIES in body: if ATTR_ADDONS_REPOSITORIES in body:
new = set(body[ATTR_ADDONS_REPOSITORIES]) new = set(body[ATTR_ADDONS_REPOSITORIES])
await asyncio.shield(self.sys_store.update_repositories(new)) await asyncio.shield(self.sys_store.update_repositories(new))
if sorted(body[ATTR_ADDONS_REPOSITORIES]) != sorted(
self.sys_config.addons_repositories
):
raise APIError("Not a valid add-on repository")
self.sys_updater.save_data() self.sys_updater.save_data()
self.sys_config.save_data() self.sys_config.save_data()

View File

@ -1,10 +1,17 @@
"""Add-on Store handler.""" """Add-on Store handler."""
import asyncio import asyncio
import logging import logging
from pathlib import Path
from typing import Dict, List from typing import Dict, List
import voluptuous as vol
from supervisor.store.validate import SCHEMA_REPOSITORY_CONFIG
from supervisor.utils.json import read_json_file
from ..const import REPOSITORY_CORE, REPOSITORY_LOCAL from ..const import REPOSITORY_CORE, REPOSITORY_LOCAL
from ..coresys import CoreSys, CoreSysAttributes from ..coresys import CoreSys, CoreSysAttributes
from ..exceptions import JsonFileError
from .addon import AddonStore from .addon import AddonStore
from .data import StoreData from .data import StoreData
from .repository import Repository from .repository import Repository
@ -60,19 +67,31 @@ class StoreManager(CoreSysAttributes):
if not await repository.load(): if not await repository.load():
_LOGGER.error("Can't load from repository %s", url) _LOGGER.error("Can't load from repository %s", url)
return return
self.repositories[url] = repository
# don't add built-in repository to config # don't add built-in repository to config
if url not in BUILTIN_REPOSITORIES: if url not in BUILTIN_REPOSITORIES:
# Verify that it is a add-on repository
repository_file = Path(repository.git.path, "repository.json")
try:
await self.sys_run_in_executor(
SCHEMA_REPOSITORY_CONFIG, read_json_file(repository_file)
)
except (JsonFileError, vol.Invalid) as err:
_LOGGER.error("%s is not a valid add-on repository. %s", url, err)
await repository.remove()
return
self.sys_config.add_addon_repository(url) self.sys_config.add_addon_repository(url)
self.repositories[url] = repository
tasks = [_add_repository(url) for url in new_rep - old_rep] tasks = [_add_repository(url) for url in new_rep - old_rep]
if tasks: if tasks:
await asyncio.wait(tasks) await asyncio.wait(tasks)
# del new repository # del new repository
for url in old_rep - new_rep - BUILTIN_REPOSITORIES: for url in old_rep - new_rep - BUILTIN_REPOSITORIES:
self.repositories.pop(url).remove() await self.repositories.pop(url).remove()
self.sys_config.drop_addon_repository(url) self.sys_config.drop_addon_repository(url)
# update data # update data

View File

@ -94,7 +94,7 @@ class GitRepo(CoreSysAttributes):
async def pull(self): async def pull(self):
"""Pull Git add-on repo.""" """Pull Git add-on repo."""
if self.lock.locked(): if self.lock.locked():
_LOGGER.warning("It is already a task in progress") _LOGGER.warning("There is already a task in progress")
return False return False
async with self.lock: async with self.lock:
@ -128,8 +128,12 @@ class GitRepo(CoreSysAttributes):
return True return True
def _remove(self): async def _remove(self):
"""Remove a repository.""" """Remove a repository."""
if self.lock.locked():
_LOGGER.warning("There is already a task in progress")
return
if not self.path.is_dir(): if not self.path.is_dir():
return return
@ -137,7 +141,9 @@ class GitRepo(CoreSysAttributes):
"""Log error.""" """Log error."""
_LOGGER.warning("Can't remove %s", path) _LOGGER.warning("Can't remove %s", path)
shutil.rmtree(self.path, onerror=log_err) await self.sys_run_in_executor(
ft.partial(shutil.rmtree, self.path, onerror=log_err)
)
class GitRepoHassIO(GitRepo): class GitRepoHassIO(GitRepo):
@ -157,7 +163,7 @@ class GitRepoCustom(GitRepo):
super().__init__(coresys, path, url) super().__init__(coresys, path, url)
def remove(self): async def remove(self):
"""Remove a custom repository.""" """Remove a custom repository."""
_LOGGER.info("Remove custom add-on repository %s", self.url) _LOGGER.info("Remove custom add-on repository %s", self.url)
self._remove() await self._remove()

View File

@ -67,9 +67,9 @@ class Repository(CoreSysAttributes):
return await self.git.pull() return await self.git.pull()
return True return True
def remove(self): async def remove(self):
"""Remove add-on repository.""" """Remove add-on repository."""
if self.slug in (REPOSITORY_CORE, REPOSITORY_LOCAL): if self.slug in (REPOSITORY_CORE, REPOSITORY_LOCAL):
raise APIError("Can't remove built-in repositories!") raise APIError("Can't remove built-in repositories!")
self.git.remove() await self.git.remove()

View File

@ -141,3 +141,12 @@ async def network_interface(dbus):
await interface.connect(dbus, "/org/freedesktop/NetworkManager/ActiveConnection/1") await interface.connect(dbus, "/org/freedesktop/NetworkManager/ActiveConnection/1")
await interface.connection.update_information() await interface.connection.update_information()
yield interface yield interface
@pytest.fixture
def store_manager(coresys: CoreSys):
"""Fixture for the store manager."""
sm_obj = coresys.store
sm_obj.repositories = set(coresys.config.addons_repositories)
with patch("supervisor.store.data.StoreData.update", return_value=MagicMock()):
yield sm_obj

View File

@ -0,0 +1,29 @@
"""Test add custom repository."""
import json
from unittest.mock import patch
import pytest
@pytest.mark.asyncio
async def test_add_valid_repository(coresys, store_manager):
"""Test add custom repository."""
current = coresys.config.addons_repositories
with patch("supervisor.store.repository.Repository.load", return_value=True), patch(
"pathlib.Path.read_text",
return_value=json.dumps({"name": "Awesome repository"}),
):
await store_manager.update_repositories(current + ["http://example.com"])
assert "http://example.com" in coresys.config.addons_repositories
@pytest.mark.asyncio
async def test_add_invalid_repository(coresys, store_manager):
"""Test add custom repository."""
current = coresys.config.addons_repositories
with patch("supervisor.store.repository.Repository.load", return_value=True), patch(
"pathlib.Path.read_text",
return_value="",
):
await store_manager.update_repositories(current + ["http://example.com"])
assert "http://example.com" not in coresys.config.addons_repositories