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:
new = set(body[ATTR_ADDONS_REPOSITORIES])
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_config.save_data()

View File

@ -1,10 +1,17 @@
"""Add-on Store handler."""
import asyncio
import logging
from pathlib import Path
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 ..coresys import CoreSys, CoreSysAttributes
from ..exceptions import JsonFileError
from .addon import AddonStore
from .data import StoreData
from .repository import Repository
@ -60,19 +67,31 @@ class StoreManager(CoreSysAttributes):
if not await repository.load():
_LOGGER.error("Can't load from repository %s", url)
return
self.repositories[url] = repository
# don't add built-in repository to config
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.repositories[url] = repository
tasks = [_add_repository(url) for url in new_rep - old_rep]
if tasks:
await asyncio.wait(tasks)
# del new repository
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)
# update data

View File

@ -94,7 +94,7 @@ class GitRepo(CoreSysAttributes):
async def pull(self):
"""Pull Git add-on repo."""
if self.lock.locked():
_LOGGER.warning("It is already a task in progress")
_LOGGER.warning("There is already a task in progress")
return False
async with self.lock:
@ -128,8 +128,12 @@ class GitRepo(CoreSysAttributes):
return True
def _remove(self):
async def _remove(self):
"""Remove a repository."""
if self.lock.locked():
_LOGGER.warning("There is already a task in progress")
return
if not self.path.is_dir():
return
@ -137,7 +141,9 @@ class GitRepo(CoreSysAttributes):
"""Log error."""
_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):
@ -157,7 +163,7 @@ class GitRepoCustom(GitRepo):
super().__init__(coresys, path, url)
def remove(self):
async def remove(self):
"""Remove a custom repository."""
_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 True
def remove(self):
async def remove(self):
"""Remove add-on repository."""
if self.slug in (REPOSITORY_CORE, REPOSITORY_LOCAL):
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.connection.update_information()
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