diff --git a/rhodecode/apps/admin/__init__.py b/rhodecode/apps/admin/__init__.py index d43e1077..2e4d7aa2 100644 --- a/rhodecode/apps/admin/__init__.py +++ b/rhodecode/apps/admin/__init__.py @@ -209,6 +209,15 @@ def admin_routes(config): renderer="rhodecode:templates/admin/settings/settings_system_update.mako", ) + config.add_route(name="settings_system_info_rcstack_update", pattern="/settings/system/rcstack/update") + config.add_view( + AdminSystemInfoSettingsView, + attr="settings_system_info_rcstack_update", + route_name="settings_system_info_rcstack_update", + request_method="GET", + renderer="rhodecode:templates/admin/settings/settings_system_update.mako", + ) + config.add_route(name="admin_settings_exception_tracker", pattern="/settings/exceptions") config.add_view( ExceptionsTrackerView, diff --git a/rhodecode/apps/admin/update_service.py b/rhodecode/apps/admin/update_service.py new file mode 100644 index 00000000..8aee9cd0 --- /dev/null +++ b/rhodecode/apps/admin/update_service.py @@ -0,0 +1,38 @@ +import logging +import os + +import requests + +from rhodecode.lib.type_utils import str2bool + + +class RcStackUpdateService: + class State: + READY = "ready" + PROCESSING = "processing" + + def __init__(self, server_host: str, server_port: int): + self.host = server_host + self.port = server_port + self.log = logging.getLogger(__name__) + + def feature_available(self): + if str2bool(os.environ.get("DISABLE_RC_UPDATE_SERVICE", "false")) or self.host is None: + return False + + try: + self.get_state() + return True + except Exception as e: + self.log.error(f"Server on host machine not available, error: {e}") + return False + + def get_state(self): + url_base = f"http://{self.host}:{self.port}" + url = f"{url_base}/state" + return requests.get(url).json().get("state", None) + + def rcstack_update(self): + url_base = f"http://{self.host}:{self.port}" + url = f"{url_base}/rcstack/update" + return requests.get(url).json() diff --git a/rhodecode/apps/admin/views/system_info.py b/rhodecode/apps/admin/views/system_info.py index ce75a584..fd68e1cd 100644 --- a/rhodecode/apps/admin/views/system_info.py +++ b/rhodecode/apps/admin/views/system_info.py @@ -22,9 +22,12 @@ import urllib.error import urllib.parse import os +from pyramid.httpexceptions import HTTPFound + import rhodecode from rhodecode.apps._base import BaseAppView from rhodecode.apps._base.navigation import navigation_list +from rhodecode.apps.admin.update_service import RcStackUpdateService from rhodecode.lib import helpers as h from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator from rhodecode.lib.utils2 import str2bool @@ -112,6 +115,20 @@ class AdminSystemInfoSettingsView(BaseAppView): update_state = ( {"type": "warning", "message": "New version available: {}".format(version)} if is_outdated else {} ) + + update_service = self._get_rc_update_service() + update_feature_available = update_service.feature_available() + + update_literal = "" + if update_feature_available: + update_service_state = update_service.get_state() + if update_service_state == RcStackUpdateService.State.READY: + update_literal = '
%s.' % (_("Update RcStack")) + else: + update_literal = "
%s." % ( + _("Update in progress, please wait. You can check logs on the host machine.") + ) + c.data_items = [ # update info ( @@ -119,6 +136,7 @@ class AdminSystemInfoSettingsView(BaseAppView): h.literal( '%s.' % (_("Check for updates")) + "
%s." % (update_info_msg) + + update_literal ), "", ), @@ -206,6 +224,22 @@ class AdminSystemInfoSettingsView(BaseAppView): h.flash("You are not allowed to do this", category="warning") return self._get_template_context(c) + @LoginRequired() + @HasPermissionAllDecorator("hg.admin") + def settings_system_info_rcstack_update(self): + update_service = self._get_rc_update_service() + update_feature_available = update_service.feature_available() + + if update_feature_available: + response = update_service.rcstack_update() + log.debug(f"update server response: {response}") + return HTTPFound(h.route_path("admin_settings_system")) + + def _get_rc_update_service(self) -> RcStackUpdateService: + host = os.environ.get("RC_UPDATE_HOST", None) + port = os.environ.get("RC_UPDATE_PORT", 10025) + return RcStackUpdateService(server_host=host, server_port=port) + @LoginRequired() @HasPermissionAllDecorator("hg.admin") def settings_system_info_check_update(self): diff --git a/rhodecode/templates/admin/settings/settings_system.mako b/rhodecode/templates/admin/settings/settings_system.mako index edaf81c0..5c359ab0 100644 --- a/rhodecode/templates/admin/settings/settings_system.mako +++ b/rhodecode/templates/admin/settings/settings_system.mako @@ -100,4 +100,7 @@ $('#update_notice').show(); $('#update_notice').load("${h.route_path('admin_settings_system_update', _query={'ver': request.GET.get('ver')})}"); }) + $('#rcstack_update').on('click', function(e){ + window.location.href = "${h.route_path('settings_system_info_rcstack_update')}"; + }) diff --git a/update_server.py b/update_server.py index feeed1fe..e0d31b2c 100644 --- a/update_server.py +++ b/update_server.py @@ -61,17 +61,11 @@ class RcstackUpdater(BaseHTTPRequestHandler): self.end_headers() self.wfile.write(body) - def _send_json_ok(self): - return self._send_json({"status": "ok"}) - def do_GET(self): - if self.path == "/health": - return self._send_json_ok() - - elif self.path == "/state": + if self.path == "/state": return self._send_json({"state": STATUS["state"]}) - elif self.path == "/update-rcstack": + elif self.path == "/rcstack/update": # If already processing, return ok and do not spawn a new process. if STATUS["state"] != "ready": return self._send_json({"status": "ok", "state": STATUS["state"]}) @@ -139,16 +133,10 @@ class RcstackUpdater(BaseHTTPRequestHandler): self.wfile.write(msg) -def main(): - logging.basicConfig( - level=logging.INFO, - format="%(asctime)s %(levelname)s %(name)s: %(message)s", - ) - host = "127.0.0.1" - port = 10025 +def main(host="127.0.0.1", port=10025): httpd = HTTPServer((host, port), RcstackUpdater) - log = logging.getLogger("mini-server") - log.info("listening on http://%s:%d", host, port) + stamp = time.strftime("%Y-%m-%d %H:%M:%S") + print("[%s] listening on http://%s:%d" % (stamp, host, port)) httpd.serve_forever()