##// END OF EJS Templates
core: remove writing of largeobject dirs on AppStartup....
marcink -
r1680:fce11c66 default
parent child Browse files
Show More
@@ -1,514 +1,513 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Pylons middleware initialization
22 Pylons middleware initialization
23 """
23 """
24 import logging
24 import logging
25 from collections import OrderedDict
25 from collections import OrderedDict
26
26
27 from paste.registry import RegistryManager
27 from paste.registry import RegistryManager
28 from paste.gzipper import make_gzip_middleware
28 from paste.gzipper import make_gzip_middleware
29 from pylons.wsgiapp import PylonsApp
29 from pylons.wsgiapp import PylonsApp
30 from pyramid.authorization import ACLAuthorizationPolicy
30 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.config import Configurator
31 from pyramid.config import Configurator
32 from pyramid.settings import asbool, aslist
32 from pyramid.settings import asbool, aslist
33 from pyramid.wsgi import wsgiapp
33 from pyramid.wsgi import wsgiapp
34 from pyramid.httpexceptions import (
34 from pyramid.httpexceptions import (
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
36 from pyramid.events import ApplicationCreated
36 from pyramid.events import ApplicationCreated
37 from pyramid.renderers import render_to_response
37 from pyramid.renderers import render_to_response
38 from routes.middleware import RoutesMiddleware
38 from routes.middleware import RoutesMiddleware
39 import routes.util
39 import routes.util
40
40
41 import rhodecode
41 import rhodecode
42 from rhodecode.model import meta
42 from rhodecode.model import meta
43 from rhodecode.config import patches
43 from rhodecode.config import patches
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
45 from rhodecode.config.environment import (
45 from rhodecode.config.environment import (
46 load_environment, load_pyramid_environment)
46 load_environment, load_pyramid_environment)
47 from rhodecode.lib.middleware import csrf
47 from rhodecode.lib.middleware import csrf
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.error_handling import (
49 from rhodecode.lib.middleware.error_handling import (
50 PylonsErrorHandlingMiddleware)
50 PylonsErrorHandlingMiddleware)
51 from rhodecode.lib.middleware.https_fixup import HttpsFixup
51 from rhodecode.lib.middleware.https_fixup import HttpsFixup
52 from rhodecode.lib.middleware.vcs import VCSMiddleware
52 from rhodecode.lib.middleware.vcs import VCSMiddleware
53 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
53 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
54 from rhodecode.lib.utils2 import aslist as rhodecode_aslist
54 from rhodecode.lib.utils2 import aslist as rhodecode_aslist
55 from rhodecode.subscribers import (
55 from rhodecode.subscribers import (
56 scan_repositories_if_enabled, write_metadata_if_needed,
56 scan_repositories_if_enabled, write_js_routes_if_enabled,
57 write_js_routes_if_enabled, create_largeobjects_dirs_if_needed)
57 write_metadata_if_needed)
58
58
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62
62
63 # this is used to avoid avoid the route lookup overhead in routesmiddleware
63 # this is used to avoid avoid the route lookup overhead in routesmiddleware
64 # for certain routes which won't go to pylons to - eg. static files, debugger
64 # for certain routes which won't go to pylons to - eg. static files, debugger
65 # it is only needed for the pylons migration and can be removed once complete
65 # it is only needed for the pylons migration and can be removed once complete
66 class SkippableRoutesMiddleware(RoutesMiddleware):
66 class SkippableRoutesMiddleware(RoutesMiddleware):
67 """ Routes middleware that allows you to skip prefixes """
67 """ Routes middleware that allows you to skip prefixes """
68
68
69 def __init__(self, *args, **kw):
69 def __init__(self, *args, **kw):
70 self.skip_prefixes = kw.pop('skip_prefixes', [])
70 self.skip_prefixes = kw.pop('skip_prefixes', [])
71 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
71 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
72
72
73 def __call__(self, environ, start_response):
73 def __call__(self, environ, start_response):
74 for prefix in self.skip_prefixes:
74 for prefix in self.skip_prefixes:
75 if environ['PATH_INFO'].startswith(prefix):
75 if environ['PATH_INFO'].startswith(prefix):
76 # added to avoid the case when a missing /_static route falls
76 # added to avoid the case when a missing /_static route falls
77 # through to pylons and causes an exception as pylons is
77 # through to pylons and causes an exception as pylons is
78 # expecting wsgiorg.routingargs to be set in the environ
78 # expecting wsgiorg.routingargs to be set in the environ
79 # by RoutesMiddleware.
79 # by RoutesMiddleware.
80 if 'wsgiorg.routing_args' not in environ:
80 if 'wsgiorg.routing_args' not in environ:
81 environ['wsgiorg.routing_args'] = (None, {})
81 environ['wsgiorg.routing_args'] = (None, {})
82 return self.app(environ, start_response)
82 return self.app(environ, start_response)
83
83
84 return super(SkippableRoutesMiddleware, self).__call__(
84 return super(SkippableRoutesMiddleware, self).__call__(
85 environ, start_response)
85 environ, start_response)
86
86
87
87
88 def make_app(global_conf, static_files=True, **app_conf):
88 def make_app(global_conf, static_files=True, **app_conf):
89 """Create a Pylons WSGI application and return it
89 """Create a Pylons WSGI application and return it
90
90
91 ``global_conf``
91 ``global_conf``
92 The inherited configuration for this application. Normally from
92 The inherited configuration for this application. Normally from
93 the [DEFAULT] section of the Paste ini file.
93 the [DEFAULT] section of the Paste ini file.
94
94
95 ``app_conf``
95 ``app_conf``
96 The application's local configuration. Normally specified in
96 The application's local configuration. Normally specified in
97 the [app:<name>] section of the Paste ini file (where <name>
97 the [app:<name>] section of the Paste ini file (where <name>
98 defaults to main).
98 defaults to main).
99
99
100 """
100 """
101 # Apply compatibility patches
101 # Apply compatibility patches
102 patches.kombu_1_5_1_python_2_7_11()
102 patches.kombu_1_5_1_python_2_7_11()
103 patches.inspect_getargspec()
103 patches.inspect_getargspec()
104
104
105 # Configure the Pylons environment
105 # Configure the Pylons environment
106 config = load_environment(global_conf, app_conf)
106 config = load_environment(global_conf, app_conf)
107
107
108 # The Pylons WSGI app
108 # The Pylons WSGI app
109 app = PylonsApp(config=config)
109 app = PylonsApp(config=config)
110 if rhodecode.is_test:
110 if rhodecode.is_test:
111 app = csrf.CSRFDetector(app)
111 app = csrf.CSRFDetector(app)
112
112
113 expected_origin = config.get('expected_origin')
113 expected_origin = config.get('expected_origin')
114 if expected_origin:
114 if expected_origin:
115 # The API can be accessed from other Origins.
115 # The API can be accessed from other Origins.
116 app = csrf.OriginChecker(app, expected_origin,
116 app = csrf.OriginChecker(app, expected_origin,
117 skip_urls=[routes.util.url_for('api')])
117 skip_urls=[routes.util.url_for('api')])
118
118
119 # Establish the Registry for this application
119 # Establish the Registry for this application
120 app = RegistryManager(app)
120 app = RegistryManager(app)
121
121
122 app.config = config
122 app.config = config
123
123
124 return app
124 return app
125
125
126
126
127 def make_pyramid_app(global_config, **settings):
127 def make_pyramid_app(global_config, **settings):
128 """
128 """
129 Constructs the WSGI application based on Pyramid and wraps the Pylons based
129 Constructs the WSGI application based on Pyramid and wraps the Pylons based
130 application.
130 application.
131
131
132 Specials:
132 Specials:
133
133
134 * We migrate from Pylons to Pyramid. While doing this, we keep both
134 * We migrate from Pylons to Pyramid. While doing this, we keep both
135 frameworks functional. This involves moving some WSGI middlewares around
135 frameworks functional. This involves moving some WSGI middlewares around
136 and providing access to some data internals, so that the old code is
136 and providing access to some data internals, so that the old code is
137 still functional.
137 still functional.
138
138
139 * The application can also be integrated like a plugin via the call to
139 * The application can also be integrated like a plugin via the call to
140 `includeme`. This is accompanied with the other utility functions which
140 `includeme`. This is accompanied with the other utility functions which
141 are called. Changing this should be done with great care to not break
141 are called. Changing this should be done with great care to not break
142 cases when these fragments are assembled from another place.
142 cases when these fragments are assembled from another place.
143
143
144 """
144 """
145 # The edition string should be available in pylons too, so we add it here
145 # The edition string should be available in pylons too, so we add it here
146 # before copying the settings.
146 # before copying the settings.
147 settings.setdefault('rhodecode.edition', 'Community Edition')
147 settings.setdefault('rhodecode.edition', 'Community Edition')
148
148
149 # As long as our Pylons application does expect "unprepared" settings, make
149 # As long as our Pylons application does expect "unprepared" settings, make
150 # sure that we keep an unmodified copy. This avoids unintentional change of
150 # sure that we keep an unmodified copy. This avoids unintentional change of
151 # behavior in the old application.
151 # behavior in the old application.
152 settings_pylons = settings.copy()
152 settings_pylons = settings.copy()
153
153
154 sanitize_settings_and_apply_defaults(settings)
154 sanitize_settings_and_apply_defaults(settings)
155 config = Configurator(settings=settings)
155 config = Configurator(settings=settings)
156 add_pylons_compat_data(config.registry, global_config, settings_pylons)
156 add_pylons_compat_data(config.registry, global_config, settings_pylons)
157
157
158 load_pyramid_environment(global_config, settings)
158 load_pyramid_environment(global_config, settings)
159
159
160 includeme_first(config)
160 includeme_first(config)
161 includeme(config)
161 includeme(config)
162 pyramid_app = config.make_wsgi_app()
162 pyramid_app = config.make_wsgi_app()
163 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
163 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
164 pyramid_app.config = config
164 pyramid_app.config = config
165
165
166 # creating the app uses a connection - return it after we are done
166 # creating the app uses a connection - return it after we are done
167 meta.Session.remove()
167 meta.Session.remove()
168
168
169 return pyramid_app
169 return pyramid_app
170
170
171
171
172 def make_not_found_view(config):
172 def make_not_found_view(config):
173 """
173 """
174 This creates the view which should be registered as not-found-view to
174 This creates the view which should be registered as not-found-view to
175 pyramid. Basically it contains of the old pylons app, converted to a view.
175 pyramid. Basically it contains of the old pylons app, converted to a view.
176 Additionally it is wrapped by some other middlewares.
176 Additionally it is wrapped by some other middlewares.
177 """
177 """
178 settings = config.registry.settings
178 settings = config.registry.settings
179 vcs_server_enabled = settings['vcs.server.enable']
179 vcs_server_enabled = settings['vcs.server.enable']
180
180
181 # Make pylons app from unprepared settings.
181 # Make pylons app from unprepared settings.
182 pylons_app = make_app(
182 pylons_app = make_app(
183 config.registry._pylons_compat_global_config,
183 config.registry._pylons_compat_global_config,
184 **config.registry._pylons_compat_settings)
184 **config.registry._pylons_compat_settings)
185 config.registry._pylons_compat_config = pylons_app.config
185 config.registry._pylons_compat_config = pylons_app.config
186
186
187 # Appenlight monitoring.
187 # Appenlight monitoring.
188 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
188 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
189 pylons_app, settings)
189 pylons_app, settings)
190
190
191 # The pylons app is executed inside of the pyramid 404 exception handler.
191 # The pylons app is executed inside of the pyramid 404 exception handler.
192 # Exceptions which are raised inside of it are not handled by pyramid
192 # Exceptions which are raised inside of it are not handled by pyramid
193 # again. Therefore we add a middleware that invokes the error handler in
193 # again. Therefore we add a middleware that invokes the error handler in
194 # case of an exception or error response. This way we return proper error
194 # case of an exception or error response. This way we return proper error
195 # HTML pages in case of an error.
195 # HTML pages in case of an error.
196 reraise = (settings.get('debugtoolbar.enabled', False) or
196 reraise = (settings.get('debugtoolbar.enabled', False) or
197 rhodecode.disable_error_handler)
197 rhodecode.disable_error_handler)
198 pylons_app = PylonsErrorHandlingMiddleware(
198 pylons_app = PylonsErrorHandlingMiddleware(
199 pylons_app, error_handler, reraise)
199 pylons_app, error_handler, reraise)
200
200
201 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
201 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
202 # view to handle the request. Therefore it is wrapped around the pylons
202 # view to handle the request. Therefore it is wrapped around the pylons
203 # app. It has to be outside of the error handling otherwise error responses
203 # app. It has to be outside of the error handling otherwise error responses
204 # from the vcsserver are converted to HTML error pages. This confuses the
204 # from the vcsserver are converted to HTML error pages. This confuses the
205 # command line tools and the user won't get a meaningful error message.
205 # command line tools and the user won't get a meaningful error message.
206 if vcs_server_enabled:
206 if vcs_server_enabled:
207 pylons_app = VCSMiddleware(
207 pylons_app = VCSMiddleware(
208 pylons_app, settings, appenlight_client, registry=config.registry)
208 pylons_app, settings, appenlight_client, registry=config.registry)
209
209
210 # Convert WSGI app to pyramid view and return it.
210 # Convert WSGI app to pyramid view and return it.
211 return wsgiapp(pylons_app)
211 return wsgiapp(pylons_app)
212
212
213
213
214 def add_pylons_compat_data(registry, global_config, settings):
214 def add_pylons_compat_data(registry, global_config, settings):
215 """
215 """
216 Attach data to the registry to support the Pylons integration.
216 Attach data to the registry to support the Pylons integration.
217 """
217 """
218 registry._pylons_compat_global_config = global_config
218 registry._pylons_compat_global_config = global_config
219 registry._pylons_compat_settings = settings
219 registry._pylons_compat_settings = settings
220
220
221
221
222 def error_handler(exception, request):
222 def error_handler(exception, request):
223 import rhodecode
223 import rhodecode
224 from rhodecode.lib.utils2 import AttributeDict
224 from rhodecode.lib.utils2 import AttributeDict
225
225
226 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
226 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
227
227
228 base_response = HTTPInternalServerError()
228 base_response = HTTPInternalServerError()
229 # prefer original exception for the response since it may have headers set
229 # prefer original exception for the response since it may have headers set
230 if isinstance(exception, HTTPException):
230 if isinstance(exception, HTTPException):
231 base_response = exception
231 base_response = exception
232
232
233 def is_http_error(response):
233 def is_http_error(response):
234 # error which should have traceback
234 # error which should have traceback
235 return response.status_code > 499
235 return response.status_code > 499
236
236
237 if is_http_error(base_response):
237 if is_http_error(base_response):
238 log.exception(
238 log.exception(
239 'error occurred handling this request for path: %s', request.path)
239 'error occurred handling this request for path: %s', request.path)
240
240
241 c = AttributeDict()
241 c = AttributeDict()
242 c.error_message = base_response.status
242 c.error_message = base_response.status
243 c.error_explanation = base_response.explanation or str(base_response)
243 c.error_explanation = base_response.explanation or str(base_response)
244 c.visual = AttributeDict()
244 c.visual = AttributeDict()
245
245
246 c.visual.rhodecode_support_url = (
246 c.visual.rhodecode_support_url = (
247 request.registry.settings.get('rhodecode_support_url') or
247 request.registry.settings.get('rhodecode_support_url') or
248 request.route_url('rhodecode_support')
248 request.route_url('rhodecode_support')
249 )
249 )
250 c.redirect_time = 0
250 c.redirect_time = 0
251 c.rhodecode_name = rhodecode_title
251 c.rhodecode_name = rhodecode_title
252 if not c.rhodecode_name:
252 if not c.rhodecode_name:
253 c.rhodecode_name = 'Rhodecode'
253 c.rhodecode_name = 'Rhodecode'
254
254
255 c.causes = []
255 c.causes = []
256 if hasattr(base_response, 'causes'):
256 if hasattr(base_response, 'causes'):
257 c.causes = base_response.causes
257 c.causes = base_response.causes
258
258
259 response = render_to_response(
259 response = render_to_response(
260 '/errors/error_document.mako', {'c': c}, request=request,
260 '/errors/error_document.mako', {'c': c}, request=request,
261 response=base_response)
261 response=base_response)
262
262
263 return response
263 return response
264
264
265
265
266 def includeme(config):
266 def includeme(config):
267 settings = config.registry.settings
267 settings = config.registry.settings
268
268
269 # plugin information
269 # plugin information
270 config.registry.rhodecode_plugins = OrderedDict()
270 config.registry.rhodecode_plugins = OrderedDict()
271
271
272 config.add_directive(
272 config.add_directive(
273 'register_rhodecode_plugin', register_rhodecode_plugin)
273 'register_rhodecode_plugin', register_rhodecode_plugin)
274
274
275 if asbool(settings.get('appenlight', 'false')):
275 if asbool(settings.get('appenlight', 'false')):
276 config.include('appenlight_client.ext.pyramid_tween')
276 config.include('appenlight_client.ext.pyramid_tween')
277
277
278 # Includes which are required. The application would fail without them.
278 # Includes which are required. The application would fail without them.
279 config.include('pyramid_mako')
279 config.include('pyramid_mako')
280 config.include('pyramid_beaker')
280 config.include('pyramid_beaker')
281
281
282 config.include('rhodecode.authentication')
282 config.include('rhodecode.authentication')
283 config.include('rhodecode.integrations')
283 config.include('rhodecode.integrations')
284
284
285 # apps
285 # apps
286 config.include('rhodecode.apps._base')
286 config.include('rhodecode.apps._base')
287 config.include('rhodecode.apps.ops')
287 config.include('rhodecode.apps.ops')
288
288
289 config.include('rhodecode.apps.admin')
289 config.include('rhodecode.apps.admin')
290 config.include('rhodecode.apps.channelstream')
290 config.include('rhodecode.apps.channelstream')
291 config.include('rhodecode.apps.login')
291 config.include('rhodecode.apps.login')
292 config.include('rhodecode.apps.home')
292 config.include('rhodecode.apps.home')
293 config.include('rhodecode.apps.repository')
293 config.include('rhodecode.apps.repository')
294 config.include('rhodecode.apps.user_profile')
294 config.include('rhodecode.apps.user_profile')
295 config.include('rhodecode.apps.my_account')
295 config.include('rhodecode.apps.my_account')
296 config.include('rhodecode.apps.svn_support')
296 config.include('rhodecode.apps.svn_support')
297
297
298 config.include('rhodecode.tweens')
298 config.include('rhodecode.tweens')
299 config.include('rhodecode.api')
299 config.include('rhodecode.api')
300
300
301 config.add_route(
301 config.add_route(
302 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
302 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
303
303
304 config.add_translation_dirs('rhodecode:i18n/')
304 config.add_translation_dirs('rhodecode:i18n/')
305 settings['default_locale_name'] = settings.get('lang', 'en')
305 settings['default_locale_name'] = settings.get('lang', 'en')
306
306
307 # Add subscribers.
307 # Add subscribers.
308 config.add_subscriber(create_largeobjects_dirs_if_needed, ApplicationCreated)
309 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
308 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
310 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
309 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
311 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
310 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
312
311
313 # Set the authorization policy.
312 # Set the authorization policy.
314 authz_policy = ACLAuthorizationPolicy()
313 authz_policy = ACLAuthorizationPolicy()
315 config.set_authorization_policy(authz_policy)
314 config.set_authorization_policy(authz_policy)
316
315
317 # Set the default renderer for HTML templates to mako.
316 # Set the default renderer for HTML templates to mako.
318 config.add_mako_renderer('.html')
317 config.add_mako_renderer('.html')
319
318
320 config.add_renderer(
319 config.add_renderer(
321 name='json_ext',
320 name='json_ext',
322 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
321 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
323
322
324 # include RhodeCode plugins
323 # include RhodeCode plugins
325 includes = aslist(settings.get('rhodecode.includes', []))
324 includes = aslist(settings.get('rhodecode.includes', []))
326 for inc in includes:
325 for inc in includes:
327 config.include(inc)
326 config.include(inc)
328
327
329 # This is the glue which allows us to migrate in chunks. By registering the
328 # This is the glue which allows us to migrate in chunks. By registering the
330 # pylons based application as the "Not Found" view in Pyramid, we will
329 # pylons based application as the "Not Found" view in Pyramid, we will
331 # fallback to the old application each time the new one does not yet know
330 # fallback to the old application each time the new one does not yet know
332 # how to handle a request.
331 # how to handle a request.
333 config.add_notfound_view(make_not_found_view(config))
332 config.add_notfound_view(make_not_found_view(config))
334
333
335 if not settings.get('debugtoolbar.enabled', False):
334 if not settings.get('debugtoolbar.enabled', False):
336 # if no toolbar, then any exception gets caught and rendered
335 # if no toolbar, then any exception gets caught and rendered
337 config.add_view(error_handler, context=Exception)
336 config.add_view(error_handler, context=Exception)
338
337
339 config.add_view(error_handler, context=HTTPError)
338 config.add_view(error_handler, context=HTTPError)
340
339
341
340
342 def includeme_first(config):
341 def includeme_first(config):
343 # redirect automatic browser favicon.ico requests to correct place
342 # redirect automatic browser favicon.ico requests to correct place
344 def favicon_redirect(context, request):
343 def favicon_redirect(context, request):
345 return HTTPFound(
344 return HTTPFound(
346 request.static_path('rhodecode:public/images/favicon.ico'))
345 request.static_path('rhodecode:public/images/favicon.ico'))
347
346
348 config.add_view(favicon_redirect, route_name='favicon')
347 config.add_view(favicon_redirect, route_name='favicon')
349 config.add_route('favicon', '/favicon.ico')
348 config.add_route('favicon', '/favicon.ico')
350
349
351 def robots_redirect(context, request):
350 def robots_redirect(context, request):
352 return HTTPFound(
351 return HTTPFound(
353 request.static_path('rhodecode:public/robots.txt'))
352 request.static_path('rhodecode:public/robots.txt'))
354
353
355 config.add_view(robots_redirect, route_name='robots')
354 config.add_view(robots_redirect, route_name='robots')
356 config.add_route('robots', '/robots.txt')
355 config.add_route('robots', '/robots.txt')
357
356
358 config.add_static_view(
357 config.add_static_view(
359 '_static/deform', 'deform:static')
358 '_static/deform', 'deform:static')
360 config.add_static_view(
359 config.add_static_view(
361 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
360 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
362
361
363
362
364 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
363 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
365 """
364 """
366 Apply outer WSGI middlewares around the application.
365 Apply outer WSGI middlewares around the application.
367
366
368 Part of this has been moved up from the Pylons layer, so that the
367 Part of this has been moved up from the Pylons layer, so that the
369 data is also available if old Pylons code is hit through an already ported
368 data is also available if old Pylons code is hit through an already ported
370 view.
369 view.
371 """
370 """
372 settings = config.registry.settings
371 settings = config.registry.settings
373
372
374 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
373 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
375 pyramid_app = HttpsFixup(pyramid_app, settings)
374 pyramid_app = HttpsFixup(pyramid_app, settings)
376
375
377 # Add RoutesMiddleware to support the pylons compatibility tween during
376 # Add RoutesMiddleware to support the pylons compatibility tween during
378 # migration to pyramid.
377 # migration to pyramid.
379 pyramid_app = SkippableRoutesMiddleware(
378 pyramid_app = SkippableRoutesMiddleware(
380 pyramid_app, config.registry._pylons_compat_config['routes.map'],
379 pyramid_app, config.registry._pylons_compat_config['routes.map'],
381 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
380 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
382
381
383 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
382 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
384
383
385 if settings['gzip_responses']:
384 if settings['gzip_responses']:
386 pyramid_app = make_gzip_middleware(
385 pyramid_app = make_gzip_middleware(
387 pyramid_app, settings, compress_level=1)
386 pyramid_app, settings, compress_level=1)
388
387
389 # this should be the outer most middleware in the wsgi stack since
388 # this should be the outer most middleware in the wsgi stack since
390 # middleware like Routes make database calls
389 # middleware like Routes make database calls
391 def pyramid_app_with_cleanup(environ, start_response):
390 def pyramid_app_with_cleanup(environ, start_response):
392 try:
391 try:
393 return pyramid_app(environ, start_response)
392 return pyramid_app(environ, start_response)
394 finally:
393 finally:
395 # Dispose current database session and rollback uncommitted
394 # Dispose current database session and rollback uncommitted
396 # transactions.
395 # transactions.
397 meta.Session.remove()
396 meta.Session.remove()
398
397
399 # In a single threaded mode server, on non sqlite db we should have
398 # In a single threaded mode server, on non sqlite db we should have
400 # '0 Current Checked out connections' at the end of a request,
399 # '0 Current Checked out connections' at the end of a request,
401 # if not, then something, somewhere is leaving a connection open
400 # if not, then something, somewhere is leaving a connection open
402 pool = meta.Base.metadata.bind.engine.pool
401 pool = meta.Base.metadata.bind.engine.pool
403 log.debug('sa pool status: %s', pool.status())
402 log.debug('sa pool status: %s', pool.status())
404
403
405
404
406 return pyramid_app_with_cleanup
405 return pyramid_app_with_cleanup
407
406
408
407
409 def sanitize_settings_and_apply_defaults(settings):
408 def sanitize_settings_and_apply_defaults(settings):
410 """
409 """
411 Applies settings defaults and does all type conversion.
410 Applies settings defaults and does all type conversion.
412
411
413 We would move all settings parsing and preparation into this place, so that
412 We would move all settings parsing and preparation into this place, so that
414 we have only one place left which deals with this part. The remaining parts
413 we have only one place left which deals with this part. The remaining parts
415 of the application would start to rely fully on well prepared settings.
414 of the application would start to rely fully on well prepared settings.
416
415
417 This piece would later be split up per topic to avoid a big fat monster
416 This piece would later be split up per topic to avoid a big fat monster
418 function.
417 function.
419 """
418 """
420
419
421 # Pyramid's mako renderer has to search in the templates folder so that the
420 # Pyramid's mako renderer has to search in the templates folder so that the
422 # old templates still work. Ported and new templates are expected to use
421 # old templates still work. Ported and new templates are expected to use
423 # real asset specifications for the includes.
422 # real asset specifications for the includes.
424 mako_directories = settings.setdefault('mako.directories', [
423 mako_directories = settings.setdefault('mako.directories', [
425 # Base templates of the original Pylons application
424 # Base templates of the original Pylons application
426 'rhodecode:templates',
425 'rhodecode:templates',
427 ])
426 ])
428 log.debug(
427 log.debug(
429 "Using the following Mako template directories: %s",
428 "Using the following Mako template directories: %s",
430 mako_directories)
429 mako_directories)
431
430
432 # Default includes, possible to change as a user
431 # Default includes, possible to change as a user
433 pyramid_includes = settings.setdefault('pyramid.includes', [
432 pyramid_includes = settings.setdefault('pyramid.includes', [
434 'rhodecode.lib.middleware.request_wrapper',
433 'rhodecode.lib.middleware.request_wrapper',
435 ])
434 ])
436 log.debug(
435 log.debug(
437 "Using the following pyramid.includes: %s",
436 "Using the following pyramid.includes: %s",
438 pyramid_includes)
437 pyramid_includes)
439
438
440 # TODO: johbo: Re-think this, usually the call to config.include
439 # TODO: johbo: Re-think this, usually the call to config.include
441 # should allow to pass in a prefix.
440 # should allow to pass in a prefix.
442 settings.setdefault('rhodecode.api.url', '/_admin/api')
441 settings.setdefault('rhodecode.api.url', '/_admin/api')
443
442
444 # Sanitize generic settings.
443 # Sanitize generic settings.
445 _list_setting(settings, 'default_encoding', 'UTF-8')
444 _list_setting(settings, 'default_encoding', 'UTF-8')
446 _bool_setting(settings, 'is_test', 'false')
445 _bool_setting(settings, 'is_test', 'false')
447 _bool_setting(settings, 'gzip_responses', 'false')
446 _bool_setting(settings, 'gzip_responses', 'false')
448
447
449 # Call split out functions that sanitize settings for each topic.
448 # Call split out functions that sanitize settings for each topic.
450 _sanitize_appenlight_settings(settings)
449 _sanitize_appenlight_settings(settings)
451 _sanitize_vcs_settings(settings)
450 _sanitize_vcs_settings(settings)
452
451
453 return settings
452 return settings
454
453
455
454
456 def _sanitize_appenlight_settings(settings):
455 def _sanitize_appenlight_settings(settings):
457 _bool_setting(settings, 'appenlight', 'false')
456 _bool_setting(settings, 'appenlight', 'false')
458
457
459
458
460 def _sanitize_vcs_settings(settings):
459 def _sanitize_vcs_settings(settings):
461 """
460 """
462 Applies settings defaults and does type conversion for all VCS related
461 Applies settings defaults and does type conversion for all VCS related
463 settings.
462 settings.
464 """
463 """
465 _string_setting(settings, 'vcs.svn.compatible_version', '')
464 _string_setting(settings, 'vcs.svn.compatible_version', '')
466 _string_setting(settings, 'git_rev_filter', '--all')
465 _string_setting(settings, 'git_rev_filter', '--all')
467 _string_setting(settings, 'vcs.hooks.protocol', 'http')
466 _string_setting(settings, 'vcs.hooks.protocol', 'http')
468 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
467 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
469 _string_setting(settings, 'vcs.server', '')
468 _string_setting(settings, 'vcs.server', '')
470 _string_setting(settings, 'vcs.server.log_level', 'debug')
469 _string_setting(settings, 'vcs.server.log_level', 'debug')
471 _string_setting(settings, 'vcs.server.protocol', 'http')
470 _string_setting(settings, 'vcs.server.protocol', 'http')
472 _bool_setting(settings, 'startup.import_repos', 'false')
471 _bool_setting(settings, 'startup.import_repos', 'false')
473 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
472 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
474 _bool_setting(settings, 'vcs.server.enable', 'true')
473 _bool_setting(settings, 'vcs.server.enable', 'true')
475 _bool_setting(settings, 'vcs.start_server', 'false')
474 _bool_setting(settings, 'vcs.start_server', 'false')
476 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
475 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
477 _int_setting(settings, 'vcs.connection_timeout', 3600)
476 _int_setting(settings, 'vcs.connection_timeout', 3600)
478
477
479 # Support legacy values of vcs.scm_app_implementation. Legacy
478 # Support legacy values of vcs.scm_app_implementation. Legacy
480 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
479 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
481 # which is now mapped to 'http'.
480 # which is now mapped to 'http'.
482 scm_app_impl = settings['vcs.scm_app_implementation']
481 scm_app_impl = settings['vcs.scm_app_implementation']
483 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
482 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
484 settings['vcs.scm_app_implementation'] = 'http'
483 settings['vcs.scm_app_implementation'] = 'http'
485
484
486
485
487 def _int_setting(settings, name, default):
486 def _int_setting(settings, name, default):
488 settings[name] = int(settings.get(name, default))
487 settings[name] = int(settings.get(name, default))
489
488
490
489
491 def _bool_setting(settings, name, default):
490 def _bool_setting(settings, name, default):
492 input = settings.get(name, default)
491 input = settings.get(name, default)
493 if isinstance(input, unicode):
492 if isinstance(input, unicode):
494 input = input.encode('utf8')
493 input = input.encode('utf8')
495 settings[name] = asbool(input)
494 settings[name] = asbool(input)
496
495
497
496
498 def _list_setting(settings, name, default):
497 def _list_setting(settings, name, default):
499 raw_value = settings.get(name, default)
498 raw_value = settings.get(name, default)
500
499
501 old_separator = ','
500 old_separator = ','
502 if old_separator in raw_value:
501 if old_separator in raw_value:
503 # If we get a comma separated list, pass it to our own function.
502 # If we get a comma separated list, pass it to our own function.
504 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
503 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
505 else:
504 else:
506 # Otherwise we assume it uses pyramids space/newline separation.
505 # Otherwise we assume it uses pyramids space/newline separation.
507 settings[name] = aslist(raw_value)
506 settings[name] = aslist(raw_value)
508
507
509
508
510 def _string_setting(settings, name, default, lower=True):
509 def _string_setting(settings, name, default, lower=True):
511 value = settings.get(name, default)
510 value = settings.get(name, default)
512 if lower:
511 if lower:
513 value = value.lower()
512 value = value.lower()
514 settings[name] = value
513 settings[name] = value
@@ -1,335 +1,308 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 import io
20 import io
21 import re
21 import re
22 import datetime
22 import datetime
23 import logging
23 import logging
24 import pylons
24 import pylons
25 import Queue
25 import Queue
26 import subprocess32
26 import subprocess32
27 import os
27 import os
28
28
29 from pyramid.i18n import get_localizer
29 from pyramid.i18n import get_localizer
30 from pyramid.threadlocal import get_current_request
30 from pyramid.threadlocal import get_current_request
31 from pyramid.interfaces import IRoutesMapper
31 from pyramid.interfaces import IRoutesMapper
32 from pyramid.settings import asbool
32 from pyramid.settings import asbool
33 from pyramid.path import AssetResolver
33 from pyramid.path import AssetResolver
34 from threading import Thread
34 from threading import Thread
35
35
36 from rhodecode.translation import _ as tsf
36 from rhodecode.translation import _ as tsf
37 from rhodecode.config.jsroutes import generate_jsroutes_content
37 from rhodecode.config.jsroutes import generate_jsroutes_content
38
38
39 import rhodecode
39 import rhodecode
40
40
41 from pylons.i18n.translation import _get_translator
41 from pylons.i18n.translation import _get_translator
42 from pylons.util import ContextObj
42 from pylons.util import ContextObj
43 from routes.util import URLGenerator
43 from routes.util import URLGenerator
44
44
45 from rhodecode.lib.base import attach_context_attributes, get_auth_user
45 from rhodecode.lib.base import attach_context_attributes, get_auth_user
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 def add_renderer_globals(event):
50 def add_renderer_globals(event):
51 # Put pylons stuff into the context. This will be removed as soon as
51 # Put pylons stuff into the context. This will be removed as soon as
52 # migration to pyramid is finished.
52 # migration to pyramid is finished.
53 conf = pylons.config._current_obj()
53 conf = pylons.config._current_obj()
54 event['h'] = conf.get('pylons.h')
54 event['h'] = conf.get('pylons.h')
55 event['c'] = pylons.tmpl_context
55 event['c'] = pylons.tmpl_context
56 event['url'] = pylons.url
56 event['url'] = pylons.url
57
57
58 # TODO: When executed in pyramid view context the request is not available
58 # TODO: When executed in pyramid view context the request is not available
59 # in the event. Find a better solution to get the request.
59 # in the event. Find a better solution to get the request.
60 request = event['request'] or get_current_request()
60 request = event['request'] or get_current_request()
61
61
62 # Add Pyramid translation as '_' to context
62 # Add Pyramid translation as '_' to context
63 event['_'] = request.translate
63 event['_'] = request.translate
64 event['_ungettext'] = request.plularize
64 event['_ungettext'] = request.plularize
65
65
66
66
67 def add_localizer(event):
67 def add_localizer(event):
68 request = event.request
68 request = event.request
69 localizer = get_localizer(request)
69 localizer = get_localizer(request)
70
70
71 def auto_translate(*args, **kwargs):
71 def auto_translate(*args, **kwargs):
72 return localizer.translate(tsf(*args, **kwargs))
72 return localizer.translate(tsf(*args, **kwargs))
73
73
74 request.localizer = localizer
74 request.localizer = localizer
75 request.translate = auto_translate
75 request.translate = auto_translate
76 request.plularize = localizer.pluralize
76 request.plularize = localizer.pluralize
77
77
78
78
79 def set_user_lang(event):
79 def set_user_lang(event):
80 request = event.request
80 request = event.request
81 cur_user = getattr(request, 'user', None)
81 cur_user = getattr(request, 'user', None)
82
82
83 if cur_user:
83 if cur_user:
84 user_lang = cur_user.get_instance().user_data.get('language')
84 user_lang = cur_user.get_instance().user_data.get('language')
85 if user_lang:
85 if user_lang:
86 log.debug('lang: setting current user:%s language to: %s', cur_user, user_lang)
86 log.debug('lang: setting current user:%s language to: %s', cur_user, user_lang)
87 event.request._LOCALE_ = user_lang
87 event.request._LOCALE_ = user_lang
88
88
89
89
90 def add_pylons_context(event):
90 def add_pylons_context(event):
91 request = event.request
91 request = event.request
92
92
93 config = rhodecode.CONFIG
93 config = rhodecode.CONFIG
94 environ = request.environ
94 environ = request.environ
95 session = request.session
95 session = request.session
96
96
97 if hasattr(request, 'vcs_call'):
97 if hasattr(request, 'vcs_call'):
98 # skip vcs calls
98 # skip vcs calls
99 return
99 return
100
100
101 # Setup pylons globals.
101 # Setup pylons globals.
102 pylons.config._push_object(config)
102 pylons.config._push_object(config)
103 pylons.request._push_object(request)
103 pylons.request._push_object(request)
104 pylons.session._push_object(session)
104 pylons.session._push_object(session)
105 pylons.translator._push_object(_get_translator(config.get('lang')))
105 pylons.translator._push_object(_get_translator(config.get('lang')))
106
106
107 pylons.url._push_object(URLGenerator(config['routes.map'], environ))
107 pylons.url._push_object(URLGenerator(config['routes.map'], environ))
108 session_key = (
108 session_key = (
109 config['pylons.environ_config'].get('session', 'beaker.session'))
109 config['pylons.environ_config'].get('session', 'beaker.session'))
110 environ[session_key] = session
110 environ[session_key] = session
111
111
112 if hasattr(request, 'rpc_method'):
112 if hasattr(request, 'rpc_method'):
113 # skip api calls
113 # skip api calls
114 return
114 return
115
115
116 # Get the rhodecode auth user object and make it available.
116 # Get the rhodecode auth user object and make it available.
117 auth_user = get_auth_user(environ)
117 auth_user = get_auth_user(environ)
118 request.user = auth_user
118 request.user = auth_user
119 environ['rc_auth_user'] = auth_user
119 environ['rc_auth_user'] = auth_user
120
120
121 # Setup the pylons context object ('c')
121 # Setup the pylons context object ('c')
122 context = ContextObj()
122 context = ContextObj()
123 context.rhodecode_user = auth_user
123 context.rhodecode_user = auth_user
124 attach_context_attributes(context, request)
124 attach_context_attributes(context, request)
125 pylons.tmpl_context._push_object(context)
125 pylons.tmpl_context._push_object(context)
126
126
127
127
128 def scan_repositories_if_enabled(event):
128 def scan_repositories_if_enabled(event):
129 """
129 """
130 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
130 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
131 does a repository scan if enabled in the settings.
131 does a repository scan if enabled in the settings.
132 """
132 """
133 from rhodecode.model.scm import ScmModel
134 from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_base_path
135 settings = event.app.registry.settings
133 settings = event.app.registry.settings
136 vcs_server_enabled = settings['vcs.server.enable']
134 vcs_server_enabled = settings['vcs.server.enable']
137 import_on_startup = settings['startup.import_repos']
135 import_on_startup = settings['startup.import_repos']
138 if vcs_server_enabled and import_on_startup:
136 if vcs_server_enabled and import_on_startup:
137 from rhodecode.model.scm import ScmModel
138 from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_base_path
139 repositories = ScmModel().repo_scan(get_rhodecode_base_path())
139 repositories = ScmModel().repo_scan(get_rhodecode_base_path())
140 repo2db_mapper(repositories, remove_obsolete=False)
140 repo2db_mapper(repositories, remove_obsolete=False)
141
141
142
142
143 def write_metadata_if_needed(event):
143 def write_metadata_if_needed(event):
144 """
144 """
145 Writes upgrade metadata
145 Writes upgrade metadata
146 """
146 """
147 import rhodecode
147 import rhodecode
148 from rhodecode.lib import system_info
148 from rhodecode.lib import system_info
149 from rhodecode.lib import ext_json
149 from rhodecode.lib import ext_json
150
150
151 def write():
151 def write():
152 fname = '.rcmetadata.json'
152 fname = '.rcmetadata.json'
153 ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__'))
153 ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__'))
154 metadata_destination = os.path.join(ini_loc, fname)
154 metadata_destination = os.path.join(ini_loc, fname)
155
155
156 configuration = system_info.SysInfo(
156 configuration = system_info.SysInfo(
157 system_info.rhodecode_config)()['value']
157 system_info.rhodecode_config)()['value']
158 license_token = configuration['config']['license_token']
158 license_token = configuration['config']['license_token']
159 dbinfo = system_info.SysInfo(system_info.database_info)()['value']
159 dbinfo = system_info.SysInfo(system_info.database_info)()['value']
160 del dbinfo['url']
160 del dbinfo['url']
161 metadata = dict(
161 metadata = dict(
162 desc='upgrade metadata info',
162 desc='upgrade metadata info',
163 license_token=license_token,
163 license_token=license_token,
164 created_on=datetime.datetime.utcnow().isoformat(),
164 created_on=datetime.datetime.utcnow().isoformat(),
165 usage=system_info.SysInfo(system_info.usage_info)()['value'],
165 usage=system_info.SysInfo(system_info.usage_info)()['value'],
166 platform=system_info.SysInfo(system_info.platform_type)()['value'],
166 platform=system_info.SysInfo(system_info.platform_type)()['value'],
167 database=dbinfo,
167 database=dbinfo,
168 cpu=system_info.SysInfo(system_info.cpu)()['value'],
168 cpu=system_info.SysInfo(system_info.cpu)()['value'],
169 memory=system_info.SysInfo(system_info.memory)()['value'],
169 memory=system_info.SysInfo(system_info.memory)()['value'],
170 )
170 )
171
171
172 with open(metadata_destination, 'wb') as f:
172 with open(metadata_destination, 'wb') as f:
173 f.write(ext_json.json.dumps(metadata))
173 f.write(ext_json.json.dumps(metadata))
174
174
175 try:
175 try:
176 write()
176 write()
177 except Exception:
177 except Exception:
178 pass
178 pass
179
179
180
180
181 def write_js_routes_if_enabled(event):
181 def write_js_routes_if_enabled(event):
182 registry = event.app.registry
182 registry = event.app.registry
183
183
184 mapper = registry.queryUtility(IRoutesMapper)
184 mapper = registry.queryUtility(IRoutesMapper)
185 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
185 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
186
186
187 def _extract_route_information(route):
187 def _extract_route_information(route):
188 """
188 """
189 Convert a route into tuple(name, path, args), eg:
189 Convert a route into tuple(name, path, args), eg:
190 ('show_user', '/profile/%(username)s', ['username'])
190 ('show_user', '/profile/%(username)s', ['username'])
191 """
191 """
192
192
193 routepath = route.pattern
193 routepath = route.pattern
194 pattern = route.pattern
194 pattern = route.pattern
195
195
196 def replace(matchobj):
196 def replace(matchobj):
197 if matchobj.group(1):
197 if matchobj.group(1):
198 return "%%(%s)s" % matchobj.group(1).split(':')[0]
198 return "%%(%s)s" % matchobj.group(1).split(':')[0]
199 else:
199 else:
200 return "%%(%s)s" % matchobj.group(2)
200 return "%%(%s)s" % matchobj.group(2)
201
201
202 routepath = _argument_prog.sub(replace, routepath)
202 routepath = _argument_prog.sub(replace, routepath)
203
203
204 return (
204 return (
205 route.name,
205 route.name,
206 routepath,
206 routepath,
207 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
207 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
208 for arg in _argument_prog.findall(pattern)]
208 for arg in _argument_prog.findall(pattern)]
209 )
209 )
210
210
211 def get_routes():
211 def get_routes():
212 # pylons routes
212 # pylons routes
213 for route in rhodecode.CONFIG['routes.map'].jsroutes():
213 for route in rhodecode.CONFIG['routes.map'].jsroutes():
214 yield route
214 yield route
215
215
216 # pyramid routes
216 # pyramid routes
217 for route in mapper.get_routes():
217 for route in mapper.get_routes():
218 if not route.name.startswith('__'):
218 if not route.name.startswith('__'):
219 yield _extract_route_information(route)
219 yield _extract_route_information(route)
220
220
221 if asbool(registry.settings.get('generate_js_files', 'false')):
221 if asbool(registry.settings.get('generate_js_files', 'false')):
222 static_path = AssetResolver().resolve('rhodecode:public').abspath()
222 static_path = AssetResolver().resolve('rhodecode:public').abspath()
223 jsroutes = get_routes()
223 jsroutes = get_routes()
224 jsroutes_file_content = generate_jsroutes_content(jsroutes)
224 jsroutes_file_content = generate_jsroutes_content(jsroutes)
225 jsroutes_file_path = os.path.join(
225 jsroutes_file_path = os.path.join(
226 static_path, 'js', 'rhodecode', 'routes.js')
226 static_path, 'js', 'rhodecode', 'routes.js')
227
227
228 with io.open(jsroutes_file_path, 'w', encoding='utf-8') as f:
228 with io.open(jsroutes_file_path, 'w', encoding='utf-8') as f:
229 f.write(jsroutes_file_content)
229 f.write(jsroutes_file_content)
230
230
231
231
232 def create_largeobjects_dirs_if_needed(event):
233 """
234 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
235 does a repository scan if enabled in the settings.
236 """
237 from rhodecode.lib.utils import get_rhodecode_base_path
238 from rhodecode.lib.vcs.backends.hg import largefiles_store
239 from rhodecode.lib.vcs.backends.git import lfs_store
240
241 repo_store_path = get_rhodecode_base_path()
242
243 paths = [
244 largefiles_store(repo_store_path),
245 lfs_store(repo_store_path)]
246
247 for path in paths:
248 if os.path.isdir(path):
249 continue
250 if os.path.isfile(path):
251 continue
252 # not a file nor dir, we try to create it
253 try:
254 os.makedirs(path)
255 except Exception:
256 log.warning('Failed to create largefiles dir:%s', path)
257
258
259 class Subscriber(object):
232 class Subscriber(object):
260 """
233 """
261 Base class for subscribers to the pyramid event system.
234 Base class for subscribers to the pyramid event system.
262 """
235 """
263 def __call__(self, event):
236 def __call__(self, event):
264 self.run(event)
237 self.run(event)
265
238
266 def run(self, event):
239 def run(self, event):
267 raise NotImplementedError('Subclass has to implement this.')
240 raise NotImplementedError('Subclass has to implement this.')
268
241
269
242
270 class AsyncSubscriber(Subscriber):
243 class AsyncSubscriber(Subscriber):
271 """
244 """
272 Subscriber that handles the execution of events in a separate task to not
245 Subscriber that handles the execution of events in a separate task to not
273 block the execution of the code which triggers the event. It puts the
246 block the execution of the code which triggers the event. It puts the
274 received events into a queue from which the worker process takes them in
247 received events into a queue from which the worker process takes them in
275 order.
248 order.
276 """
249 """
277 def __init__(self):
250 def __init__(self):
278 self._stop = False
251 self._stop = False
279 self._eventq = Queue.Queue()
252 self._eventq = Queue.Queue()
280 self._worker = self.create_worker()
253 self._worker = self.create_worker()
281 self._worker.start()
254 self._worker.start()
282
255
283 def __call__(self, event):
256 def __call__(self, event):
284 self._eventq.put(event)
257 self._eventq.put(event)
285
258
286 def create_worker(self):
259 def create_worker(self):
287 worker = Thread(target=self.do_work)
260 worker = Thread(target=self.do_work)
288 worker.daemon = True
261 worker.daemon = True
289 return worker
262 return worker
290
263
291 def stop_worker(self):
264 def stop_worker(self):
292 self._stop = False
265 self._stop = False
293 self._eventq.put(None)
266 self._eventq.put(None)
294 self._worker.join()
267 self._worker.join()
295
268
296 def do_work(self):
269 def do_work(self):
297 while not self._stop:
270 while not self._stop:
298 event = self._eventq.get()
271 event = self._eventq.get()
299 if event is not None:
272 if event is not None:
300 self.run(event)
273 self.run(event)
301
274
302
275
303 class AsyncSubprocessSubscriber(AsyncSubscriber):
276 class AsyncSubprocessSubscriber(AsyncSubscriber):
304 """
277 """
305 Subscriber that uses the subprocess32 module to execute a command if an
278 Subscriber that uses the subprocess32 module to execute a command if an
306 event is received. Events are handled asynchronously.
279 event is received. Events are handled asynchronously.
307 """
280 """
308
281
309 def __init__(self, cmd, timeout=None):
282 def __init__(self, cmd, timeout=None):
310 super(AsyncSubprocessSubscriber, self).__init__()
283 super(AsyncSubprocessSubscriber, self).__init__()
311 self._cmd = cmd
284 self._cmd = cmd
312 self._timeout = timeout
285 self._timeout = timeout
313
286
314 def run(self, event):
287 def run(self, event):
315 cmd = self._cmd
288 cmd = self._cmd
316 timeout = self._timeout
289 timeout = self._timeout
317 log.debug('Executing command %s.', cmd)
290 log.debug('Executing command %s.', cmd)
318
291
319 try:
292 try:
320 output = subprocess32.check_output(
293 output = subprocess32.check_output(
321 cmd, timeout=timeout, stderr=subprocess32.STDOUT)
294 cmd, timeout=timeout, stderr=subprocess32.STDOUT)
322 log.debug('Command finished %s', cmd)
295 log.debug('Command finished %s', cmd)
323 if output:
296 if output:
324 log.debug('Command output: %s', output)
297 log.debug('Command output: %s', output)
325 except subprocess32.TimeoutExpired as e:
298 except subprocess32.TimeoutExpired as e:
326 log.exception('Timeout while executing command.')
299 log.exception('Timeout while executing command.')
327 if e.output:
300 if e.output:
328 log.error('Command output: %s', e.output)
301 log.error('Command output: %s', e.output)
329 except subprocess32.CalledProcessError as e:
302 except subprocess32.CalledProcessError as e:
330 log.exception('Error while executing command.')
303 log.exception('Error while executing command.')
331 if e.output:
304 if e.output:
332 log.error('Command output: %s', e.output)
305 log.error('Command output: %s', e.output)
333 except:
306 except:
334 log.exception(
307 log.exception(
335 'Exception while executing command %s.', cmd)
308 'Exception while executing command %s.', cmd)
General Comments 0
You need to be logged in to leave comments. Login now