-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Standalone report versions and available updates (#1603)
* simpler * simpler * final version * better naming * Simpler * Simpler * simpler * add cachetools dependency * add types * Increase coverage * Fix * simpler * rename * Add short timeout to pypi * simpler naming
- Loading branch information
Showing
9 changed files
with
310 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# Copyright (C) 2021 Bosutech XXI S.L. | ||
# | ||
# nucliadb is offered under the AGPL v3.0 and as commercial software. | ||
# For commercial licensing, contact us at [email protected]. | ||
# | ||
# AGPL: | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as | ||
# published by the Free Software Foundation, either version 3 of the | ||
# License, or (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
import httpx | ||
|
||
PYPI_JSON_API = "https://pypi.org/pypi/{package_name}/json" | ||
|
||
|
||
class PyPi: | ||
def __init__(self, timeout_seconds: int = 2): | ||
self.session = httpx.AsyncClient(timeout=timeout_seconds) | ||
|
||
async def __aenter__(self): | ||
return self | ||
|
||
async def __aexit__(self, *exc): | ||
await self.close() | ||
|
||
async def close(self): | ||
await self.session.aclose() | ||
|
||
async def get_latest_version(self, package_name: str) -> str: | ||
response = await self.session.get( | ||
PYPI_JSON_API.format(package_name=package_name), | ||
headers={"Accept": "application/json"}, | ||
) | ||
response.raise_for_status() | ||
return response.json()["info"]["version"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
# Copyright (C) 2021 Bosutech XXI S.L. | ||
# | ||
# nucliadb is offered under the AGPL v3.0 and as commercial software. | ||
# For commercial licensing, contact us at [email protected]. | ||
# | ||
# AGPL: | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as | ||
# published by the Free Software Foundation, either version 3 of the | ||
# License, or (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
|
||
from unittest import mock | ||
|
||
import pkg_resources | ||
import pytest | ||
|
||
from nucliadb.standalone.versions import ( | ||
get_latest_version, | ||
installed_nucliadb, | ||
is_newer_release, | ||
latest_nucliadb, | ||
) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
"installed,latest,expected", | ||
[ | ||
("1.1.1", "1.1.1.post1", False), | ||
("1.1.1", "1.1.1", False), | ||
("1.1.1", "1.1.0", False), | ||
("1.1.1", "1.0.1", False), | ||
("1.1.1", "0.1.1", False), | ||
("1.1.1", "1.1.2", True), | ||
("1.1.1", "1.2.1", True), | ||
("1.1.1", "2.1.1", True), | ||
], | ||
) | ||
def test_is_newer_release(installed, latest, expected): | ||
assert is_newer_release(installed, latest) is expected | ||
|
||
|
||
def test_installed_nucliadb(): | ||
pkg_resources.parse_version(installed_nucliadb()) | ||
|
||
|
||
@pytest.fixture() | ||
def pypi_mock(): | ||
version = "1.0.0" | ||
with mock.patch( | ||
"nucliadb.standalone.versions._get_latest_version", return_value=version | ||
): | ||
yield | ||
|
||
|
||
async def test_latest_nucliadb(pypi_mock): | ||
assert await latest_nucliadb() == "1.0.0" | ||
|
||
|
||
async def test_get_latest_version(pypi_mock): | ||
assert await get_latest_version("foobar") == "1.0.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
# Copyright (C) 2021 Bosutech XXI S.L. | ||
# | ||
# nucliadb is offered under the AGPL v3.0 and as commercial software. | ||
# For commercial licensing, contact us at [email protected]. | ||
# | ||
# AGPL: | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as | ||
# published by the Free Software Foundation, either version 3 of the | ||
# License, or (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
import enum | ||
import logging | ||
from typing import Optional | ||
|
||
import pkg_resources | ||
from cachetools import TTLCache | ||
|
||
from nucliadb.common.http_clients.pypi import PyPi | ||
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
CACHE_TTL_SECONDS = 30 * 60 # 30 minutes | ||
CACHE = TTLCache(maxsize=128, ttl=CACHE_TTL_SECONDS) # type: ignore | ||
|
||
|
||
class StandalonePackages(enum.Enum): | ||
NUCLIADB = "nucliadb" | ||
NUCLIADB_ADMIN_ASSETS = "nucliadb-admin-assets" | ||
|
||
|
||
WatchedPackages = [pkg.value for pkg in StandalonePackages] | ||
|
||
|
||
def installed_nucliadb() -> str: | ||
return get_installed_version(StandalonePackages.NUCLIADB.value) | ||
|
||
|
||
async def latest_nucliadb() -> Optional[str]: | ||
return await get_latest_version(StandalonePackages.NUCLIADB.value) | ||
|
||
|
||
def nucliadb_updates_available(installed: str, latest: Optional[str]) -> bool: | ||
if latest is None: | ||
return False | ||
return is_newer_release(installed, latest) | ||
|
||
|
||
def is_newer_release(installed: str, latest: str) -> bool: | ||
""" | ||
Returns true if the latest version is newer than the installed version. | ||
>>> is_newer_release("1.2.3", "1.2.4") | ||
True | ||
>>> is_newer_release("1.2.3", "1.2.3") | ||
False | ||
>>> is_newer_release("1.2.3", "1.2.3.post1") | ||
False | ||
""" | ||
parsed_installed = pkg_resources.parse_version(_release(installed)) | ||
parsed_latest = pkg_resources.parse_version(_release(latest)) | ||
return parsed_latest > parsed_installed | ||
|
||
|
||
def _release(version: str) -> str: | ||
""" | ||
Strips the .postX part of the version so that wecan compare major.minor.patch only. | ||
>>> _release("1.2.3") | ||
'1.2.3' | ||
>>> _release("1.2.3.post1") | ||
'1.2.3' | ||
""" | ||
return version.split(".post")[0] | ||
|
||
|
||
def get_installed_version(package_name: str) -> str: | ||
return pkg_resources.get_distribution(package_name).version | ||
|
||
|
||
async def get_latest_version(package: str) -> Optional[str]: | ||
result = CACHE.get(package, None) | ||
if result is None: | ||
try: | ||
result = await _get_latest_version(package) | ||
except Exception as exc: | ||
logger.warning(f"Error getting latest {package} version", exc_info=exc) | ||
return None | ||
CACHE[package] = result | ||
return result | ||
|
||
|
||
async def _get_latest_version(package_name: str) -> str: | ||
async with PyPi() as pypi: | ||
return await pypi.get_latest_version(package_name) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# Copyright (C) 2021 Bosutech XXI S.L. | ||
# | ||
# nucliadb is offered under the AGPL v3.0 and as commercial software. | ||
# For commercial licensing, contact us at [email protected]. | ||
# | ||
# AGPL: | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU Affero General Public License as | ||
# published by the Free Software Foundation, either version 3 of the | ||
# License, or (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU Affero General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License | ||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
from unittest import mock | ||
|
||
import pytest | ||
|
||
from nucliadb.common.http_clients import pypi | ||
|
||
|
||
class TestPyPi: | ||
@pytest.fixture() | ||
def response(self): | ||
resp = mock.Mock() | ||
resp.status = 200 | ||
resp.json.return_value = {"info": {"version": "1.0.0"}} | ||
yield resp | ||
|
||
@pytest.fixture() | ||
def client(self, response): | ||
cl = pypi.PyPi() | ||
cl.session = mock.MagicMock() | ||
cl.session.get = mock.AsyncMock() | ||
cl.session.aclose = mock.AsyncMock() | ||
cl.session.get.return_value = response | ||
yield cl | ||
|
||
@pytest.mark.asyncio | ||
async def test_get_latest_version(self, client): | ||
assert await client.get_latest_version("foo") == "1.0.0" | ||
|
||
@pytest.mark.asyncio | ||
async def test_context_manager(self, client): | ||
async with client: | ||
pass | ||
client.session.aclose.assert_awaited_once() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,4 +59,6 @@ idna>=3.3 | |
sniffio>=1.2.0 | ||
|
||
async_lru==2.0.2 | ||
cachetools==5.3.2 | ||
types-cachetools==5.3.0.5 | ||
kubernetes_asyncio |