Show More
@@ -0,0 +1,71 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2013-2017 RhodeCode GmbH | |||
|
4 | # | |||
|
5 | # This program is free software: you can redistribute it and/or modify | |||
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |||
|
7 | # (only), as published by the Free Software Foundation. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
14 | # You should have received a copy of the GNU Affero General Public License | |||
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
|
16 | # | |||
|
17 | # This program is dual-licensed. If you wish to learn more about the | |||
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |||
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |||
|
20 | ||||
|
21 | import logging | |||
|
22 | import urllib2 | |||
|
23 | from packaging import version | |||
|
24 | ||||
|
25 | import rhodecode | |||
|
26 | from rhodecode.lib.ext_json import json | |||
|
27 | from rhodecode.model import BaseModel | |||
|
28 | from rhodecode.model.meta import Session | |||
|
29 | from rhodecode.model.settings import SettingsModel | |||
|
30 | ||||
|
31 | ||||
|
32 | log = logging.getLogger(__name__) | |||
|
33 | ||||
|
34 | ||||
|
35 | class UpdateModel(BaseModel): | |||
|
36 | UPDATE_SETTINGS_KEY = 'update_version' | |||
|
37 | UPDATE_URL_SETTINGS_KEY = 'rhodecode_update_url' | |||
|
38 | ||||
|
39 | @staticmethod | |||
|
40 | def get_update_data(update_url): | |||
|
41 | """Return the JSON update data.""" | |||
|
42 | ver = rhodecode.__version__ | |||
|
43 | log.debug('Checking for upgrade on `%s` server', update_url) | |||
|
44 | opener = urllib2.build_opener() | |||
|
45 | opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)] | |||
|
46 | response = opener.open(update_url) | |||
|
47 | response_data = response.read() | |||
|
48 | data = json.loads(response_data) | |||
|
49 | log.debug('update server returned data') | |||
|
50 | return data | |||
|
51 | ||||
|
52 | def get_update_url(self): | |||
|
53 | settings = SettingsModel().get_all_settings() | |||
|
54 | return settings.get(self.UPDATE_URL_SETTINGS_KEY) | |||
|
55 | ||||
|
56 | def store_version(self, version): | |||
|
57 | log.debug('Storing version %s into settings', version) | |||
|
58 | setting = SettingsModel().create_or_update_setting( | |||
|
59 | self.UPDATE_SETTINGS_KEY, version) | |||
|
60 | Session().add(setting) | |||
|
61 | Session().commit() | |||
|
62 | ||||
|
63 | def get_stored_version(self): | |||
|
64 | obj = SettingsModel().get_setting_by_name(self.UPDATE_SETTINGS_KEY) | |||
|
65 | if obj: | |||
|
66 | return obj.app_settings_value | |||
|
67 | return '0.0.0' | |||
|
68 | ||||
|
69 | def is_outdated(self, cur_version, latest_version=None): | |||
|
70 | latest_version = latest_version or self.get_stored_version() | |||
|
71 | return version.Version(latest_version) > version.Version(cur_version) |
@@ -31,8 +31,7 b' from rhodecode.tests import assert_sessi' | |||||
31 | from rhodecode.tests.utils import AssertResponse |
|
31 | from rhodecode.tests.utils import AssertResponse | |
32 |
|
32 | |||
33 |
|
33 | |||
34 | UPDATE_DATA_QUALNAME = ( |
|
34 | UPDATE_DATA_QUALNAME = 'rhodecode.model.update.UpdateModel.get_update_data' | |
35 | 'rhodecode.apps.admin.views.system_info.AdminSystemInfoSettingsView.get_update_data') |
|
|||
36 |
|
35 | |||
37 |
|
36 | |||
38 | def route_path(name, params=None, **kwargs): |
|
37 | def route_path(name, params=None, **kwargs): | |
@@ -586,7 +585,7 b' class TestAdminSystemInfo(object):' | |||||
586 | with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data): |
|
585 | with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data): | |
587 | response = self.app.get(route_path('admin_settings_system_update')) |
|
586 | response = self.app.get(route_path('admin_settings_system_update')) | |
588 | response.mustcontain( |
|
587 | response.mustcontain( | |
589 |
' |
|
588 | 'This instance is already running the <b>latest</b> stable version') | |
590 |
|
589 | |||
591 | def test_system_update_bad_response(self, autologin_user): |
|
590 | def test_system_update_bad_response(self, autologin_user): | |
592 | with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')): |
|
591 | with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')): |
@@ -20,7 +20,6 b'' | |||||
20 |
|
20 | |||
21 | import logging |
|
21 | import logging | |
22 | import urllib2 |
|
22 | import urllib2 | |
23 | import packaging.version |
|
|||
24 |
|
23 | |||
25 | from pyramid.view import view_config |
|
24 | from pyramid.view import view_config | |
26 |
|
25 | |||
@@ -31,8 +30,7 b' from rhodecode.lib import helpers as h' | |||||
31 | from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator) |
|
30 | from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator) | |
32 | from rhodecode.lib.utils2 import str2bool |
|
31 | from rhodecode.lib.utils2 import str2bool | |
33 | from rhodecode.lib import system_info |
|
32 | from rhodecode.lib import system_info | |
34 |
from rhodecode.l |
|
33 | from rhodecode.model.update import UpdateModel | |
35 | from rhodecode.model.settings import SettingsModel |
|
|||
36 |
|
34 | |||
37 | log = logging.getLogger(__name__) |
|
35 | log = logging.getLogger(__name__) | |
38 |
|
36 | |||
@@ -40,26 +38,8 b' log = logging.getLogger(__name__)' | |||||
40 | class AdminSystemInfoSettingsView(BaseAppView): |
|
38 | class AdminSystemInfoSettingsView(BaseAppView): | |
41 | def load_default_context(self): |
|
39 | def load_default_context(self): | |
42 | c = self._get_local_tmpl_context() |
|
40 | c = self._get_local_tmpl_context() | |
43 |
|
||||
44 | return c |
|
41 | return c | |
45 |
|
42 | |||
46 | @staticmethod |
|
|||
47 | def get_update_data(update_url): |
|
|||
48 | """Return the JSON update data.""" |
|
|||
49 | ver = rhodecode.__version__ |
|
|||
50 | log.debug('Checking for upgrade on `%s` server', update_url) |
|
|||
51 | opener = urllib2.build_opener() |
|
|||
52 | opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)] |
|
|||
53 | response = opener.open(update_url) |
|
|||
54 | response_data = response.read() |
|
|||
55 | data = json.loads(response_data) |
|
|||
56 |
|
||||
57 | return data |
|
|||
58 |
|
||||
59 | def get_update_url(self): |
|
|||
60 | settings = SettingsModel().get_all_settings() |
|
|||
61 | return settings.get('rhodecode_update_url') |
|
|||
62 |
|
||||
63 | @LoginRequired() |
|
43 | @LoginRequired() | |
64 | @HasPermissionAllDecorator('hg.admin') |
|
44 | @HasPermissionAllDecorator('hg.admin') | |
65 | @view_config( |
|
45 | @view_config( | |
@@ -77,7 +57,7 b' class AdminSystemInfoSettingsView(BaseAp' | |||||
77 |
|
57 | |||
78 | snapshot = str2bool(self.request.params.get('snapshot')) |
|
58 | snapshot = str2bool(self.request.params.get('snapshot')) | |
79 |
|
59 | |||
80 |
c.rhodecode_update_url = |
|
60 | c.rhodecode_update_url = UpdateModel().get_update_url() | |
81 | server_info = system_info.get_system_info(self.request.environ) |
|
61 | server_info = system_info.get_system_info(self.request.environ) | |
82 |
|
62 | |||
83 | for key, val in server_info.items(): |
|
63 | for key, val in server_info.items(): | |
@@ -97,6 +77,14 b' class AdminSystemInfoSettingsView(BaseAp' | |||||
97 | update_info_msg = _('Note: please make sure this server can ' |
|
77 | update_info_msg = _('Note: please make sure this server can ' | |
98 | 'access `${url}` for the update link to work', |
|
78 | 'access `${url}` for the update link to work', | |
99 | mapping=dict(url=c.rhodecode_update_url)) |
|
79 | mapping=dict(url=c.rhodecode_update_url)) | |
|
80 | version = UpdateModel().get_stored_version() | |||
|
81 | is_outdated = UpdateModel().is_outdated( | |||
|
82 | rhodecode.__version__, version) | |||
|
83 | update_state = { | |||
|
84 | 'type': 'warning', | |||
|
85 | 'message': 'New version available: {}'.format(version) | |||
|
86 | } \ | |||
|
87 | if is_outdated else {} | |||
100 | c.data_items = [ |
|
88 | c.data_items = [ | |
101 | # update info |
|
89 | # update info | |
102 | (_('Update info'), h.literal( |
|
90 | (_('Update info'), h.literal( | |
@@ -107,6 +95,7 b' class AdminSystemInfoSettingsView(BaseAp' | |||||
107 |
|
95 | |||
108 | # RhodeCode specific |
|
96 | # RhodeCode specific | |
109 | (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')), |
|
97 | (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')), | |
|
98 | (_('Latest version'), version, update_state), | |||
110 | (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')), |
|
99 | (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')), | |
111 | (_('RhodeCode Server ID'), val('server')['server_id'], state('server')), |
|
100 | (_('RhodeCode Server ID'), val('server')['server_id'], state('server')), | |
112 | (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')), |
|
101 | (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')), | |
@@ -178,11 +167,11 b' class AdminSystemInfoSettingsView(BaseAp' | |||||
178 | _ = self.request.translate |
|
167 | _ = self.request.translate | |
179 | c = self.load_default_context() |
|
168 | c = self.load_default_context() | |
180 |
|
169 | |||
181 |
update_url = |
|
170 | update_url = UpdateModel().get_update_url() | |
182 |
|
171 | |||
183 | _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">{}</div>'.format(s) |
|
172 | _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">{}</div>'.format(s) | |
184 | try: |
|
173 | try: | |
185 |
data = |
|
174 | data = UpdateModel().get_update_data(update_url) | |
186 | except urllib2.URLError as e: |
|
175 | except urllib2.URLError as e: | |
187 | log.exception("Exception contacting upgrade server") |
|
176 | log.exception("Exception contacting upgrade server") | |
188 | self.request.override_renderer = 'string' |
|
177 | self.request.override_renderer = 'string' | |
@@ -200,9 +189,9 b' class AdminSystemInfoSettingsView(BaseAp' | |||||
200 | c.cur_ver = rhodecode.__version__ |
|
189 | c.cur_ver = rhodecode.__version__ | |
201 | c.should_upgrade = False |
|
190 | c.should_upgrade = False | |
202 |
|
191 | |||
203 | if (packaging.version.Version(c.latest_ver) > |
|
192 | is_oudated = UpdateModel().is_outdated(c.cur_ver, c.latest_ver) | |
204 | packaging.version.Version(c.cur_ver)): |
|
193 | if is_oudated: | |
205 | c.should_upgrade = True |
|
194 | c.should_upgrade = True | |
206 | c.important_notices = latest['general'] |
|
195 | c.important_notices = latest['general'] | |
207 |
|
196 | UpdateModel().store_version(latest['version']) | ||
208 | return self._get_template_context(c) |
|
197 | return self._get_template_context(c) |
@@ -278,6 +278,20 b' def sync_repo(*args, **kwargs):' | |||||
278 | log.debug('Repo `%s` not found or without a clone_url', repo_name) |
|
278 | log.debug('Repo `%s` not found or without a clone_url', repo_name) | |
279 |
|
279 | |||
280 |
|
280 | |||
|
281 | @async_task(ignore_result=True) | |||
|
282 | def check_for_update(): | |||
|
283 | from rhodecode.model.update import UpdateModel | |||
|
284 | update_url = UpdateModel().get_update_url() | |||
|
285 | cur_ver = rhodecode.__version__ | |||
|
286 | ||||
|
287 | try: | |||
|
288 | data = UpdateModel().get_update_data(update_url) | |||
|
289 | latest = data['versions'][0] | |||
|
290 | UpdateModel().store_version(latest['version']) | |||
|
291 | except Exception: | |||
|
292 | pass | |||
|
293 | ||||
|
294 | ||||
281 | @async_task(ignore_result=False) |
|
295 | @async_task(ignore_result=False) | |
282 | def beat_check(*args, **kwargs): |
|
296 | def beat_check(*args, **kwargs): | |
283 | log = get_logger(beat_check) |
|
297 | log = get_logger(beat_check) |
@@ -2,8 +2,7 b'' | |||||
2 | ## upgrade block rendered afte on-click check |
|
2 | ## upgrade block rendered afte on-click check | |
3 |
|
3 | |||
4 | <div class="alert ${'alert-warning' if c.should_upgrade else 'alert-success'}"> |
|
4 | <div class="alert ${'alert-warning' if c.should_upgrade else 'alert-success'}"> | |
5 |
<p |
|
5 | <p> | |
6 |
|
||||
7 | %if c.should_upgrade: |
|
6 | %if c.should_upgrade: | |
8 | A <b>new version</b> is available: |
|
7 | A <b>new version</b> is available: | |
9 | %if c.latest_data.get('title'): |
|
8 | %if c.latest_data.get('title'): | |
@@ -12,7 +11,7 b'' | |||||
12 | <b>${c.latest_ver}</b> |
|
11 | <b>${c.latest_ver}</b> | |
13 | %endif |
|
12 | %endif | |
14 | %else: |
|
13 | %else: | |
15 |
|
|
14 | This instance is already running the <b>latest</b> stable version ${c.latest_ver}. | |
16 | %endif |
|
15 | %endif | |
17 | </p> |
|
16 | </p> | |
18 |
|
17 |
General Comments 0
You need to be logged in to leave comments.
Login now