2019-10-28 19:49:16 +00:00
|
|
|
import os
|
|
|
|
from threading import Thread
|
|
|
|
from time import sleep
|
|
|
|
from typing import Optional
|
|
|
|
|
|
|
|
import attr
|
|
|
|
import requests
|
|
|
|
from semantic_version import Version
|
|
|
|
|
2021-01-05 14:44:31 +00:00
|
|
|
from apiserver.config_repo import config
|
2021-01-05 14:28:49 +00:00
|
|
|
from apiserver.config.info import get_version
|
|
|
|
from apiserver.database.model.settings import Settings
|
|
|
|
from apiserver.utilities.threads_manager import ThreadsManager
|
2019-10-28 19:49:16 +00:00
|
|
|
|
|
|
|
log = config.logger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
class CheckUpdatesThread(Thread):
|
|
|
|
_enabled = bool(config.get("apiserver.check_for_updates.enabled", True))
|
|
|
|
|
|
|
|
@attr.s(auto_attribs=True)
|
|
|
|
class _VersionResponse:
|
|
|
|
version: str
|
|
|
|
patch_upgrade: bool
|
|
|
|
description: str = None
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
super(CheckUpdatesThread, self).__init__(
|
|
|
|
target=self._check_updates, daemon=True
|
|
|
|
)
|
|
|
|
|
|
|
|
def start(self) -> None:
|
|
|
|
if not self._enabled:
|
|
|
|
log.info("Checking for updates is disabled")
|
|
|
|
return
|
|
|
|
super(CheckUpdatesThread, self).start()
|
|
|
|
|
|
|
|
@property
|
|
|
|
def component_name(self) -> str:
|
|
|
|
return config.get("apiserver.check_for_updates.component_name", "trains-server")
|
|
|
|
|
|
|
|
def _check_new_version_available(self) -> Optional[_VersionResponse]:
|
|
|
|
url = config.get(
|
2019-12-14 21:33:04 +00:00
|
|
|
"apiserver.check_for_updates.url",
|
|
|
|
"https://updates.trains.allegro.ai/updates",
|
2019-10-28 19:49:16 +00:00
|
|
|
)
|
|
|
|
|
2019-12-14 21:33:04 +00:00
|
|
|
uid = Settings.get_by_key("server.uuid")
|
|
|
|
|
2019-10-28 19:49:16 +00:00
|
|
|
response = requests.get(
|
|
|
|
url,
|
2019-12-21 16:13:05 +00:00
|
|
|
json={"versions": {self.component_name: str(get_version())}, "uid": uid},
|
2019-10-28 19:49:16 +00:00
|
|
|
timeout=float(
|
|
|
|
config.get("apiserver.check_for_updates.request_timeout_sec", 3.0)
|
|
|
|
),
|
|
|
|
)
|
|
|
|
|
|
|
|
if not response.ok:
|
|
|
|
return
|
|
|
|
|
|
|
|
response = response.json().get(self.component_name)
|
|
|
|
if not response:
|
|
|
|
return
|
|
|
|
|
|
|
|
latest_version = response.get("version")
|
|
|
|
if not latest_version:
|
|
|
|
return
|
|
|
|
|
2019-12-21 16:13:05 +00:00
|
|
|
cur_version = Version(get_version())
|
2019-10-28 19:49:16 +00:00
|
|
|
latest_version = Version(latest_version)
|
|
|
|
if cur_version >= latest_version:
|
|
|
|
return
|
|
|
|
|
|
|
|
return self._VersionResponse(
|
|
|
|
version=str(latest_version),
|
|
|
|
patch_upgrade=(
|
|
|
|
latest_version.major == cur_version.major
|
|
|
|
and latest_version.minor == cur_version.minor
|
|
|
|
),
|
|
|
|
description=response.get("description").split("\r\n"),
|
|
|
|
)
|
|
|
|
|
|
|
|
def _check_updates(self):
|
2019-12-29 07:04:07 +00:00
|
|
|
update_interval_sec = max(
|
|
|
|
float(
|
|
|
|
config.get(
|
|
|
|
"apiserver.check_for_updates.check_interval_sec",
|
|
|
|
60 * 60 * 24,
|
|
|
|
)
|
|
|
|
),
|
|
|
|
60 * 5,
|
|
|
|
)
|
|
|
|
while not ThreadsManager.terminating:
|
2019-10-28 19:49:16 +00:00
|
|
|
# noinspection PyBroadException
|
|
|
|
try:
|
|
|
|
response = self._check_new_version_available()
|
|
|
|
if response:
|
|
|
|
if response.patch_upgrade:
|
|
|
|
log.info(
|
|
|
|
f"{self.component_name.upper()} new package available: upgrade to v{response.version} "
|
|
|
|
f"is recommended!\nRelease Notes:\n{os.linesep.join(response.description)}"
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
log.info(
|
|
|
|
f"{self.component_name.upper()} new version available: upgrade to v{response.version}"
|
|
|
|
f" is recommended!"
|
|
|
|
)
|
|
|
|
except Exception:
|
|
|
|
log.exception("Failed obtaining updates")
|
|
|
|
|
2019-12-29 07:04:07 +00:00
|
|
|
sleep(update_interval_sec)
|
2019-10-28 19:49:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
check_updates_thread = CheckUpdatesThread()
|