##// END OF EJS Templates
exception_tracker: use a default value of exception store that is working across all instances e.g vcsserver and enterprise.
marcink -
r3065:fbb1492a default
parent child Browse files
Show More
@@ -1,582 +1,582
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 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 import os
21 import os
22 import sys
22 import sys
23 import logging
23 import logging
24 import collections
24 import collections
25 import tempfile
25 import tempfile
26
26
27 from paste.gzipper import make_gzip_middleware
27 from paste.gzipper import make_gzip_middleware
28 import pyramid.events
28 import pyramid.events
29 from pyramid.wsgi import wsgiapp
29 from pyramid.wsgi import wsgiapp
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.httpexceptions import (
33 from pyramid.httpexceptions import (
34 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
34 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
35 from pyramid.renderers import render_to_response
35 from pyramid.renderers import render_to_response
36
36
37 from rhodecode.model import meta
37 from rhodecode.model import meta
38 from rhodecode.config import patches
38 from rhodecode.config import patches
39 from rhodecode.config import utils as config_utils
39 from rhodecode.config import utils as config_utils
40 from rhodecode.config.environment import load_pyramid_environment
40 from rhodecode.config.environment import load_pyramid_environment
41
41
42 import rhodecode.events
42 import rhodecode.events
43 from rhodecode.lib.middleware.vcs import VCSMiddleware
43 from rhodecode.lib.middleware.vcs import VCSMiddleware
44 from rhodecode.lib.request import Request
44 from rhodecode.lib.request import Request
45 from rhodecode.lib.vcs import VCSCommunicationError
45 from rhodecode.lib.vcs import VCSCommunicationError
46 from rhodecode.lib.exceptions import VCSServerUnavailable
46 from rhodecode.lib.exceptions import VCSServerUnavailable
47 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
47 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.https_fixup import HttpsFixup
48 from rhodecode.lib.middleware.https_fixup import HttpsFixup
49 from rhodecode.lib.celerylib.loader import configure_celery
49 from rhodecode.lib.celerylib.loader import configure_celery
50 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
50 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
51 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
51 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
52 from rhodecode.lib.exc_tracking import store_exception
52 from rhodecode.lib.exc_tracking import store_exception
53 from rhodecode.subscribers import (
53 from rhodecode.subscribers import (
54 scan_repositories_if_enabled, write_js_routes_if_enabled,
54 scan_repositories_if_enabled, write_js_routes_if_enabled,
55 write_metadata_if_needed, inject_app_settings)
55 write_metadata_if_needed, inject_app_settings)
56
56
57
57
58 log = logging.getLogger(__name__)
58 log = logging.getLogger(__name__)
59
59
60
60
61 def is_http_error(response):
61 def is_http_error(response):
62 # error which should have traceback
62 # error which should have traceback
63 return response.status_code > 499
63 return response.status_code > 499
64
64
65
65
66 def make_pyramid_app(global_config, **settings):
66 def make_pyramid_app(global_config, **settings):
67 """
67 """
68 Constructs the WSGI application based on Pyramid.
68 Constructs the WSGI application based on Pyramid.
69
69
70 Specials:
70 Specials:
71
71
72 * The application can also be integrated like a plugin via the call to
72 * The application can also be integrated like a plugin via the call to
73 `includeme`. This is accompanied with the other utility functions which
73 `includeme`. This is accompanied with the other utility functions which
74 are called. Changing this should be done with great care to not break
74 are called. Changing this should be done with great care to not break
75 cases when these fragments are assembled from another place.
75 cases when these fragments are assembled from another place.
76
76
77 """
77 """
78
78
79 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
79 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
80 # will be replaced by the value of the environment variable "NAME" in this case.
80 # will be replaced by the value of the environment variable "NAME" in this case.
81 environ = {
81 environ = {
82 'ENV_{}'.format(key): value for key, value in os.environ.items()}
82 'ENV_{}'.format(key): value for key, value in os.environ.items()}
83
83
84 global_config = _substitute_values(global_config, environ)
84 global_config = _substitute_values(global_config, environ)
85 settings = _substitute_values(settings, environ)
85 settings = _substitute_values(settings, environ)
86
86
87 sanitize_settings_and_apply_defaults(settings)
87 sanitize_settings_and_apply_defaults(settings)
88
88
89 config = Configurator(settings=settings)
89 config = Configurator(settings=settings)
90
90
91 # Apply compatibility patches
91 # Apply compatibility patches
92 patches.inspect_getargspec()
92 patches.inspect_getargspec()
93
93
94 load_pyramid_environment(global_config, settings)
94 load_pyramid_environment(global_config, settings)
95
95
96 # Static file view comes first
96 # Static file view comes first
97 includeme_first(config)
97 includeme_first(config)
98
98
99 includeme(config)
99 includeme(config)
100
100
101 pyramid_app = config.make_wsgi_app()
101 pyramid_app = config.make_wsgi_app()
102 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
102 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
103 pyramid_app.config = config
103 pyramid_app.config = config
104
104
105 config.configure_celery(global_config['__file__'])
105 config.configure_celery(global_config['__file__'])
106 # creating the app uses a connection - return it after we are done
106 # creating the app uses a connection - return it after we are done
107 meta.Session.remove()
107 meta.Session.remove()
108
108
109 log.info('Pyramid app %s created and configured.', pyramid_app)
109 log.info('Pyramid app %s created and configured.', pyramid_app)
110 return pyramid_app
110 return pyramid_app
111
111
112
112
113 def not_found_view(request):
113 def not_found_view(request):
114 """
114 """
115 This creates the view which should be registered as not-found-view to
115 This creates the view which should be registered as not-found-view to
116 pyramid.
116 pyramid.
117 """
117 """
118
118
119 if not getattr(request, 'vcs_call', None):
119 if not getattr(request, 'vcs_call', None):
120 # handle like regular case with our error_handler
120 # handle like regular case with our error_handler
121 return error_handler(HTTPNotFound(), request)
121 return error_handler(HTTPNotFound(), request)
122
122
123 # handle not found view as a vcs call
123 # handle not found view as a vcs call
124 settings = request.registry.settings
124 settings = request.registry.settings
125 ae_client = getattr(request, 'ae_client', None)
125 ae_client = getattr(request, 'ae_client', None)
126 vcs_app = VCSMiddleware(
126 vcs_app = VCSMiddleware(
127 HTTPNotFound(), request.registry, settings,
127 HTTPNotFound(), request.registry, settings,
128 appenlight_client=ae_client)
128 appenlight_client=ae_client)
129
129
130 return wsgiapp(vcs_app)(None, request)
130 return wsgiapp(vcs_app)(None, request)
131
131
132
132
133 def error_handler(exception, request):
133 def error_handler(exception, request):
134 import rhodecode
134 import rhodecode
135 from rhodecode.lib import helpers
135 from rhodecode.lib import helpers
136
136
137 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
137 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
138
138
139 base_response = HTTPInternalServerError()
139 base_response = HTTPInternalServerError()
140 # prefer original exception for the response since it may have headers set
140 # prefer original exception for the response since it may have headers set
141 if isinstance(exception, HTTPException):
141 if isinstance(exception, HTTPException):
142 base_response = exception
142 base_response = exception
143 elif isinstance(exception, VCSCommunicationError):
143 elif isinstance(exception, VCSCommunicationError):
144 base_response = VCSServerUnavailable()
144 base_response = VCSServerUnavailable()
145
145
146 if is_http_error(base_response):
146 if is_http_error(base_response):
147 log.exception(
147 log.exception(
148 'error occurred handling this request for path: %s', request.path)
148 'error occurred handling this request for path: %s', request.path)
149
149
150 error_explanation = base_response.explanation or str(base_response)
150 error_explanation = base_response.explanation or str(base_response)
151 if base_response.status_code == 404:
151 if base_response.status_code == 404:
152 error_explanation += " Or you don't have permission to access it."
152 error_explanation += " Or you don't have permission to access it."
153 c = AttributeDict()
153 c = AttributeDict()
154 c.error_message = base_response.status
154 c.error_message = base_response.status
155 c.error_explanation = error_explanation
155 c.error_explanation = error_explanation
156 c.visual = AttributeDict()
156 c.visual = AttributeDict()
157
157
158 c.visual.rhodecode_support_url = (
158 c.visual.rhodecode_support_url = (
159 request.registry.settings.get('rhodecode_support_url') or
159 request.registry.settings.get('rhodecode_support_url') or
160 request.route_url('rhodecode_support')
160 request.route_url('rhodecode_support')
161 )
161 )
162 c.redirect_time = 0
162 c.redirect_time = 0
163 c.rhodecode_name = rhodecode_title
163 c.rhodecode_name = rhodecode_title
164 if not c.rhodecode_name:
164 if not c.rhodecode_name:
165 c.rhodecode_name = 'Rhodecode'
165 c.rhodecode_name = 'Rhodecode'
166
166
167 c.causes = []
167 c.causes = []
168 if is_http_error(base_response):
168 if is_http_error(base_response):
169 c.causes.append('Server is overloaded.')
169 c.causes.append('Server is overloaded.')
170 c.causes.append('Server database connection is lost.')
170 c.causes.append('Server database connection is lost.')
171 c.causes.append('Server expected unhandled error.')
171 c.causes.append('Server expected unhandled error.')
172
172
173 if hasattr(base_response, 'causes'):
173 if hasattr(base_response, 'causes'):
174 c.causes = base_response.causes
174 c.causes = base_response.causes
175
175
176 c.messages = helpers.flash.pop_messages(request=request)
176 c.messages = helpers.flash.pop_messages(request=request)
177
177
178 exc_info = sys.exc_info()
178 exc_info = sys.exc_info()
179 c.exception_id = id(exc_info)
179 c.exception_id = id(exc_info)
180 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
180 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
181 or base_response.status_code > 499
181 or base_response.status_code > 499
182 c.exception_id_url = request.route_url(
182 c.exception_id_url = request.route_url(
183 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
183 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
184
184
185 if c.show_exception_id:
185 if c.show_exception_id:
186 store_exception(c.exception_id, exc_info)
186 store_exception(c.exception_id, exc_info)
187
187
188 response = render_to_response(
188 response = render_to_response(
189 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
189 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
190 response=base_response)
190 response=base_response)
191
191
192 return response
192 return response
193
193
194
194
195 def includeme_first(config):
195 def includeme_first(config):
196 # redirect automatic browser favicon.ico requests to correct place
196 # redirect automatic browser favicon.ico requests to correct place
197 def favicon_redirect(context, request):
197 def favicon_redirect(context, request):
198 return HTTPFound(
198 return HTTPFound(
199 request.static_path('rhodecode:public/images/favicon.ico'))
199 request.static_path('rhodecode:public/images/favicon.ico'))
200
200
201 config.add_view(favicon_redirect, route_name='favicon')
201 config.add_view(favicon_redirect, route_name='favicon')
202 config.add_route('favicon', '/favicon.ico')
202 config.add_route('favicon', '/favicon.ico')
203
203
204 def robots_redirect(context, request):
204 def robots_redirect(context, request):
205 return HTTPFound(
205 return HTTPFound(
206 request.static_path('rhodecode:public/robots.txt'))
206 request.static_path('rhodecode:public/robots.txt'))
207
207
208 config.add_view(robots_redirect, route_name='robots')
208 config.add_view(robots_redirect, route_name='robots')
209 config.add_route('robots', '/robots.txt')
209 config.add_route('robots', '/robots.txt')
210
210
211 config.add_static_view(
211 config.add_static_view(
212 '_static/deform', 'deform:static')
212 '_static/deform', 'deform:static')
213 config.add_static_view(
213 config.add_static_view(
214 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
214 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
215
215
216
216
217 def includeme(config):
217 def includeme(config):
218 settings = config.registry.settings
218 settings = config.registry.settings
219 config.set_request_factory(Request)
219 config.set_request_factory(Request)
220
220
221 # plugin information
221 # plugin information
222 config.registry.rhodecode_plugins = collections.OrderedDict()
222 config.registry.rhodecode_plugins = collections.OrderedDict()
223
223
224 config.add_directive(
224 config.add_directive(
225 'register_rhodecode_plugin', register_rhodecode_plugin)
225 'register_rhodecode_plugin', register_rhodecode_plugin)
226
226
227 config.add_directive('configure_celery', configure_celery)
227 config.add_directive('configure_celery', configure_celery)
228
228
229 if asbool(settings.get('appenlight', 'false')):
229 if asbool(settings.get('appenlight', 'false')):
230 config.include('appenlight_client.ext.pyramid_tween')
230 config.include('appenlight_client.ext.pyramid_tween')
231
231
232 # Includes which are required. The application would fail without them.
232 # Includes which are required. The application would fail without them.
233 config.include('pyramid_mako')
233 config.include('pyramid_mako')
234 config.include('pyramid_beaker')
234 config.include('pyramid_beaker')
235 config.include('rhodecode.lib.rc_cache')
235 config.include('rhodecode.lib.rc_cache')
236
236
237 config.include('rhodecode.authentication')
237 config.include('rhodecode.authentication')
238 config.include('rhodecode.integrations')
238 config.include('rhodecode.integrations')
239
239
240 # apps
240 # apps
241 config.include('rhodecode.apps._base')
241 config.include('rhodecode.apps._base')
242 config.include('rhodecode.apps.ops')
242 config.include('rhodecode.apps.ops')
243
243
244 config.include('rhodecode.apps.admin')
244 config.include('rhodecode.apps.admin')
245 config.include('rhodecode.apps.channelstream')
245 config.include('rhodecode.apps.channelstream')
246 config.include('rhodecode.apps.login')
246 config.include('rhodecode.apps.login')
247 config.include('rhodecode.apps.home')
247 config.include('rhodecode.apps.home')
248 config.include('rhodecode.apps.journal')
248 config.include('rhodecode.apps.journal')
249 config.include('rhodecode.apps.repository')
249 config.include('rhodecode.apps.repository')
250 config.include('rhodecode.apps.repo_group')
250 config.include('rhodecode.apps.repo_group')
251 config.include('rhodecode.apps.user_group')
251 config.include('rhodecode.apps.user_group')
252 config.include('rhodecode.apps.search')
252 config.include('rhodecode.apps.search')
253 config.include('rhodecode.apps.user_profile')
253 config.include('rhodecode.apps.user_profile')
254 config.include('rhodecode.apps.user_group_profile')
254 config.include('rhodecode.apps.user_group_profile')
255 config.include('rhodecode.apps.my_account')
255 config.include('rhodecode.apps.my_account')
256 config.include('rhodecode.apps.svn_support')
256 config.include('rhodecode.apps.svn_support')
257 config.include('rhodecode.apps.ssh_support')
257 config.include('rhodecode.apps.ssh_support')
258 config.include('rhodecode.apps.gist')
258 config.include('rhodecode.apps.gist')
259
259
260 config.include('rhodecode.apps.debug_style')
260 config.include('rhodecode.apps.debug_style')
261 config.include('rhodecode.tweens')
261 config.include('rhodecode.tweens')
262 config.include('rhodecode.api')
262 config.include('rhodecode.api')
263
263
264 config.add_route(
264 config.add_route(
265 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
265 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
266
266
267 config.add_translation_dirs('rhodecode:i18n/')
267 config.add_translation_dirs('rhodecode:i18n/')
268 settings['default_locale_name'] = settings.get('lang', 'en')
268 settings['default_locale_name'] = settings.get('lang', 'en')
269
269
270 # Add subscribers.
270 # Add subscribers.
271 config.add_subscriber(inject_app_settings,
271 config.add_subscriber(inject_app_settings,
272 pyramid.events.ApplicationCreated)
272 pyramid.events.ApplicationCreated)
273 config.add_subscriber(scan_repositories_if_enabled,
273 config.add_subscriber(scan_repositories_if_enabled,
274 pyramid.events.ApplicationCreated)
274 pyramid.events.ApplicationCreated)
275 config.add_subscriber(write_metadata_if_needed,
275 config.add_subscriber(write_metadata_if_needed,
276 pyramid.events.ApplicationCreated)
276 pyramid.events.ApplicationCreated)
277 config.add_subscriber(write_js_routes_if_enabled,
277 config.add_subscriber(write_js_routes_if_enabled,
278 pyramid.events.ApplicationCreated)
278 pyramid.events.ApplicationCreated)
279
279
280 # request custom methods
280 # request custom methods
281 config.add_request_method(
281 config.add_request_method(
282 'rhodecode.lib.partial_renderer.get_partial_renderer',
282 'rhodecode.lib.partial_renderer.get_partial_renderer',
283 'get_partial_renderer')
283 'get_partial_renderer')
284
284
285 # Set the authorization policy.
285 # Set the authorization policy.
286 authz_policy = ACLAuthorizationPolicy()
286 authz_policy = ACLAuthorizationPolicy()
287 config.set_authorization_policy(authz_policy)
287 config.set_authorization_policy(authz_policy)
288
288
289 # Set the default renderer for HTML templates to mako.
289 # Set the default renderer for HTML templates to mako.
290 config.add_mako_renderer('.html')
290 config.add_mako_renderer('.html')
291
291
292 config.add_renderer(
292 config.add_renderer(
293 name='json_ext',
293 name='json_ext',
294 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
294 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
295
295
296 # include RhodeCode plugins
296 # include RhodeCode plugins
297 includes = aslist(settings.get('rhodecode.includes', []))
297 includes = aslist(settings.get('rhodecode.includes', []))
298 for inc in includes:
298 for inc in includes:
299 config.include(inc)
299 config.include(inc)
300
300
301 # custom not found view, if our pyramid app doesn't know how to handle
301 # custom not found view, if our pyramid app doesn't know how to handle
302 # the request pass it to potential VCS handling ap
302 # the request pass it to potential VCS handling ap
303 config.add_notfound_view(not_found_view)
303 config.add_notfound_view(not_found_view)
304 if not settings.get('debugtoolbar.enabled', False):
304 if not settings.get('debugtoolbar.enabled', False):
305 # disabled debugtoolbar handle all exceptions via the error_handlers
305 # disabled debugtoolbar handle all exceptions via the error_handlers
306 config.add_view(error_handler, context=Exception)
306 config.add_view(error_handler, context=Exception)
307
307
308 # all errors including 403/404/50X
308 # all errors including 403/404/50X
309 config.add_view(error_handler, context=HTTPError)
309 config.add_view(error_handler, context=HTTPError)
310
310
311
311
312 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
312 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
313 """
313 """
314 Apply outer WSGI middlewares around the application.
314 Apply outer WSGI middlewares around the application.
315 """
315 """
316 registry = config.registry
316 registry = config.registry
317 settings = registry.settings
317 settings = registry.settings
318
318
319 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
319 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
320 pyramid_app = HttpsFixup(pyramid_app, settings)
320 pyramid_app = HttpsFixup(pyramid_app, settings)
321
321
322 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
322 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
323 pyramid_app, settings)
323 pyramid_app, settings)
324 registry.ae_client = _ae_client
324 registry.ae_client = _ae_client
325
325
326 if settings['gzip_responses']:
326 if settings['gzip_responses']:
327 pyramid_app = make_gzip_middleware(
327 pyramid_app = make_gzip_middleware(
328 pyramid_app, settings, compress_level=1)
328 pyramid_app, settings, compress_level=1)
329
329
330 # this should be the outer most middleware in the wsgi stack since
330 # this should be the outer most middleware in the wsgi stack since
331 # middleware like Routes make database calls
331 # middleware like Routes make database calls
332 def pyramid_app_with_cleanup(environ, start_response):
332 def pyramid_app_with_cleanup(environ, start_response):
333 try:
333 try:
334 return pyramid_app(environ, start_response)
334 return pyramid_app(environ, start_response)
335 finally:
335 finally:
336 # Dispose current database session and rollback uncommitted
336 # Dispose current database session and rollback uncommitted
337 # transactions.
337 # transactions.
338 meta.Session.remove()
338 meta.Session.remove()
339
339
340 # In a single threaded mode server, on non sqlite db we should have
340 # In a single threaded mode server, on non sqlite db we should have
341 # '0 Current Checked out connections' at the end of a request,
341 # '0 Current Checked out connections' at the end of a request,
342 # if not, then something, somewhere is leaving a connection open
342 # if not, then something, somewhere is leaving a connection open
343 pool = meta.Base.metadata.bind.engine.pool
343 pool = meta.Base.metadata.bind.engine.pool
344 log.debug('sa pool status: %s', pool.status())
344 log.debug('sa pool status: %s', pool.status())
345 log.debug('Request processing finalized')
345 log.debug('Request processing finalized')
346
346
347 return pyramid_app_with_cleanup
347 return pyramid_app_with_cleanup
348
348
349
349
350 def sanitize_settings_and_apply_defaults(settings):
350 def sanitize_settings_and_apply_defaults(settings):
351 """
351 """
352 Applies settings defaults and does all type conversion.
352 Applies settings defaults and does all type conversion.
353
353
354 We would move all settings parsing and preparation into this place, so that
354 We would move all settings parsing and preparation into this place, so that
355 we have only one place left which deals with this part. The remaining parts
355 we have only one place left which deals with this part. The remaining parts
356 of the application would start to rely fully on well prepared settings.
356 of the application would start to rely fully on well prepared settings.
357
357
358 This piece would later be split up per topic to avoid a big fat monster
358 This piece would later be split up per topic to avoid a big fat monster
359 function.
359 function.
360 """
360 """
361
361
362 settings.setdefault('rhodecode.edition', 'Community Edition')
362 settings.setdefault('rhodecode.edition', 'Community Edition')
363
363
364 if 'mako.default_filters' not in settings:
364 if 'mako.default_filters' not in settings:
365 # set custom default filters if we don't have it defined
365 # set custom default filters if we don't have it defined
366 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
366 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
367 settings['mako.default_filters'] = 'h_filter'
367 settings['mako.default_filters'] = 'h_filter'
368
368
369 if 'mako.directories' not in settings:
369 if 'mako.directories' not in settings:
370 mako_directories = settings.setdefault('mako.directories', [
370 mako_directories = settings.setdefault('mako.directories', [
371 # Base templates of the original application
371 # Base templates of the original application
372 'rhodecode:templates',
372 'rhodecode:templates',
373 ])
373 ])
374 log.debug(
374 log.debug(
375 "Using the following Mako template directories: %s",
375 "Using the following Mako template directories: %s",
376 mako_directories)
376 mako_directories)
377
377
378 # Default includes, possible to change as a user
378 # Default includes, possible to change as a user
379 pyramid_includes = settings.setdefault('pyramid.includes', [
379 pyramid_includes = settings.setdefault('pyramid.includes', [
380 'rhodecode.lib.middleware.request_wrapper',
380 'rhodecode.lib.middleware.request_wrapper',
381 ])
381 ])
382 log.debug(
382 log.debug(
383 "Using the following pyramid.includes: %s",
383 "Using the following pyramid.includes: %s",
384 pyramid_includes)
384 pyramid_includes)
385
385
386 # TODO: johbo: Re-think this, usually the call to config.include
386 # TODO: johbo: Re-think this, usually the call to config.include
387 # should allow to pass in a prefix.
387 # should allow to pass in a prefix.
388 settings.setdefault('rhodecode.api.url', '/_admin/api')
388 settings.setdefault('rhodecode.api.url', '/_admin/api')
389
389
390 # Sanitize generic settings.
390 # Sanitize generic settings.
391 _list_setting(settings, 'default_encoding', 'UTF-8')
391 _list_setting(settings, 'default_encoding', 'UTF-8')
392 _bool_setting(settings, 'is_test', 'false')
392 _bool_setting(settings, 'is_test', 'false')
393 _bool_setting(settings, 'gzip_responses', 'false')
393 _bool_setting(settings, 'gzip_responses', 'false')
394
394
395 # Call split out functions that sanitize settings for each topic.
395 # Call split out functions that sanitize settings for each topic.
396 _sanitize_appenlight_settings(settings)
396 _sanitize_appenlight_settings(settings)
397 _sanitize_vcs_settings(settings)
397 _sanitize_vcs_settings(settings)
398 _sanitize_cache_settings(settings)
398 _sanitize_cache_settings(settings)
399
399
400 # configure instance id
400 # configure instance id
401 config_utils.set_instance_id(settings)
401 config_utils.set_instance_id(settings)
402
402
403 return settings
403 return settings
404
404
405
405
406 def _sanitize_appenlight_settings(settings):
406 def _sanitize_appenlight_settings(settings):
407 _bool_setting(settings, 'appenlight', 'false')
407 _bool_setting(settings, 'appenlight', 'false')
408
408
409
409
410 def _sanitize_vcs_settings(settings):
410 def _sanitize_vcs_settings(settings):
411 """
411 """
412 Applies settings defaults and does type conversion for all VCS related
412 Applies settings defaults and does type conversion for all VCS related
413 settings.
413 settings.
414 """
414 """
415 _string_setting(settings, 'vcs.svn.compatible_version', '')
415 _string_setting(settings, 'vcs.svn.compatible_version', '')
416 _string_setting(settings, 'git_rev_filter', '--all')
416 _string_setting(settings, 'git_rev_filter', '--all')
417 _string_setting(settings, 'vcs.hooks.protocol', 'http')
417 _string_setting(settings, 'vcs.hooks.protocol', 'http')
418 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
418 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
419 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
419 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
420 _string_setting(settings, 'vcs.server', '')
420 _string_setting(settings, 'vcs.server', '')
421 _string_setting(settings, 'vcs.server.log_level', 'debug')
421 _string_setting(settings, 'vcs.server.log_level', 'debug')
422 _string_setting(settings, 'vcs.server.protocol', 'http')
422 _string_setting(settings, 'vcs.server.protocol', 'http')
423 _bool_setting(settings, 'startup.import_repos', 'false')
423 _bool_setting(settings, 'startup.import_repos', 'false')
424 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
424 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
425 _bool_setting(settings, 'vcs.server.enable', 'true')
425 _bool_setting(settings, 'vcs.server.enable', 'true')
426 _bool_setting(settings, 'vcs.start_server', 'false')
426 _bool_setting(settings, 'vcs.start_server', 'false')
427 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
427 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
428 _int_setting(settings, 'vcs.connection_timeout', 3600)
428 _int_setting(settings, 'vcs.connection_timeout', 3600)
429
429
430 # Support legacy values of vcs.scm_app_implementation. Legacy
430 # Support legacy values of vcs.scm_app_implementation. Legacy
431 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
431 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
432 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
432 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
433 scm_app_impl = settings['vcs.scm_app_implementation']
433 scm_app_impl = settings['vcs.scm_app_implementation']
434 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
434 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
435 settings['vcs.scm_app_implementation'] = 'http'
435 settings['vcs.scm_app_implementation'] = 'http'
436
436
437
437
438 def _sanitize_cache_settings(settings):
438 def _sanitize_cache_settings(settings):
439
439 temp_store = tempfile.gettempdir()
440 default_cache_dir = os.path.join(tempfile.gettempdir(), 'rc_cache')
440 default_cache_dir = os.path.join(temp_store, 'rc_cache')
441
441
442 # save default, cache dir, and use it for all backends later.
442 # save default, cache dir, and use it for all backends later.
443 default_cache_dir = _string_setting(
443 default_cache_dir = _string_setting(
444 settings,
444 settings,
445 'cache_dir',
445 'cache_dir',
446 default_cache_dir, lower=False, default_when_empty=True)
446 default_cache_dir, lower=False, default_when_empty=True)
447
447
448 # ensure we have our dir created
448 # ensure we have our dir created
449 if not os.path.isdir(default_cache_dir):
449 if not os.path.isdir(default_cache_dir):
450 os.makedirs(default_cache_dir, mode=0755)
450 os.makedirs(default_cache_dir, mode=0755)
451
451
452 # exception store cache
452 # exception store cache
453 _string_setting(
453 _string_setting(
454 settings,
454 settings,
455 'exception_tracker.store_path',
455 'exception_tracker.store_path',
456 default_cache_dir, lower=False)
456 temp_store, lower=False, default_when_empty=True)
457
457
458 # cache_perms
458 # cache_perms
459 _string_setting(
459 _string_setting(
460 settings,
460 settings,
461 'rc_cache.cache_perms.backend',
461 'rc_cache.cache_perms.backend',
462 'dogpile.cache.rc.file_namespace', lower=False)
462 'dogpile.cache.rc.file_namespace', lower=False)
463 _int_setting(
463 _int_setting(
464 settings,
464 settings,
465 'rc_cache.cache_perms.expiration_time',
465 'rc_cache.cache_perms.expiration_time',
466 60)
466 60)
467 _string_setting(
467 _string_setting(
468 settings,
468 settings,
469 'rc_cache.cache_perms.arguments.filename',
469 'rc_cache.cache_perms.arguments.filename',
470 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
470 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
471
471
472 # cache_repo
472 # cache_repo
473 _string_setting(
473 _string_setting(
474 settings,
474 settings,
475 'rc_cache.cache_repo.backend',
475 'rc_cache.cache_repo.backend',
476 'dogpile.cache.rc.file_namespace', lower=False)
476 'dogpile.cache.rc.file_namespace', lower=False)
477 _int_setting(
477 _int_setting(
478 settings,
478 settings,
479 'rc_cache.cache_repo.expiration_time',
479 'rc_cache.cache_repo.expiration_time',
480 60)
480 60)
481 _string_setting(
481 _string_setting(
482 settings,
482 settings,
483 'rc_cache.cache_repo.arguments.filename',
483 'rc_cache.cache_repo.arguments.filename',
484 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
484 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
485
485
486 # cache_license
486 # cache_license
487 _string_setting(
487 _string_setting(
488 settings,
488 settings,
489 'rc_cache.cache_license.backend',
489 'rc_cache.cache_license.backend',
490 'dogpile.cache.rc.file_namespace', lower=False)
490 'dogpile.cache.rc.file_namespace', lower=False)
491 _int_setting(
491 _int_setting(
492 settings,
492 settings,
493 'rc_cache.cache_license.expiration_time',
493 'rc_cache.cache_license.expiration_time',
494 5*60)
494 5*60)
495 _string_setting(
495 _string_setting(
496 settings,
496 settings,
497 'rc_cache.cache_license.arguments.filename',
497 'rc_cache.cache_license.arguments.filename',
498 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
498 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
499
499
500 # cache_repo_longterm memory, 96H
500 # cache_repo_longterm memory, 96H
501 _string_setting(
501 _string_setting(
502 settings,
502 settings,
503 'rc_cache.cache_repo_longterm.backend',
503 'rc_cache.cache_repo_longterm.backend',
504 'dogpile.cache.rc.memory_lru', lower=False)
504 'dogpile.cache.rc.memory_lru', lower=False)
505 _int_setting(
505 _int_setting(
506 settings,
506 settings,
507 'rc_cache.cache_repo_longterm.expiration_time',
507 'rc_cache.cache_repo_longterm.expiration_time',
508 345600)
508 345600)
509 _int_setting(
509 _int_setting(
510 settings,
510 settings,
511 'rc_cache.cache_repo_longterm.max_size',
511 'rc_cache.cache_repo_longterm.max_size',
512 10000)
512 10000)
513
513
514 # sql_cache_short
514 # sql_cache_short
515 _string_setting(
515 _string_setting(
516 settings,
516 settings,
517 'rc_cache.sql_cache_short.backend',
517 'rc_cache.sql_cache_short.backend',
518 'dogpile.cache.rc.memory_lru', lower=False)
518 'dogpile.cache.rc.memory_lru', lower=False)
519 _int_setting(
519 _int_setting(
520 settings,
520 settings,
521 'rc_cache.sql_cache_short.expiration_time',
521 'rc_cache.sql_cache_short.expiration_time',
522 30)
522 30)
523 _int_setting(
523 _int_setting(
524 settings,
524 settings,
525 'rc_cache.sql_cache_short.max_size',
525 'rc_cache.sql_cache_short.max_size',
526 10000)
526 10000)
527
527
528
528
529 def _int_setting(settings, name, default):
529 def _int_setting(settings, name, default):
530 settings[name] = int(settings.get(name, default))
530 settings[name] = int(settings.get(name, default))
531 return settings[name]
531 return settings[name]
532
532
533
533
534 def _bool_setting(settings, name, default):
534 def _bool_setting(settings, name, default):
535 input_val = settings.get(name, default)
535 input_val = settings.get(name, default)
536 if isinstance(input_val, unicode):
536 if isinstance(input_val, unicode):
537 input_val = input_val.encode('utf8')
537 input_val = input_val.encode('utf8')
538 settings[name] = asbool(input_val)
538 settings[name] = asbool(input_val)
539 return settings[name]
539 return settings[name]
540
540
541
541
542 def _list_setting(settings, name, default):
542 def _list_setting(settings, name, default):
543 raw_value = settings.get(name, default)
543 raw_value = settings.get(name, default)
544
544
545 old_separator = ','
545 old_separator = ','
546 if old_separator in raw_value:
546 if old_separator in raw_value:
547 # If we get a comma separated list, pass it to our own function.
547 # If we get a comma separated list, pass it to our own function.
548 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
548 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
549 else:
549 else:
550 # Otherwise we assume it uses pyramids space/newline separation.
550 # Otherwise we assume it uses pyramids space/newline separation.
551 settings[name] = aslist(raw_value)
551 settings[name] = aslist(raw_value)
552 return settings[name]
552 return settings[name]
553
553
554
554
555 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
555 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
556 value = settings.get(name, default)
556 value = settings.get(name, default)
557
557
558 if default_when_empty and not value:
558 if default_when_empty and not value:
559 # use default value when value is empty
559 # use default value when value is empty
560 value = default
560 value = default
561
561
562 if lower:
562 if lower:
563 value = value.lower()
563 value = value.lower()
564 settings[name] = value
564 settings[name] = value
565 return settings[name]
565 return settings[name]
566
566
567
567
568 def _substitute_values(mapping, substitutions):
568 def _substitute_values(mapping, substitutions):
569
569
570 try:
570 try:
571 result = {
571 result = {
572 # Note: Cannot use regular replacements, since they would clash
572 # Note: Cannot use regular replacements, since they would clash
573 # with the implementation of ConfigParser. Using "format" instead.
573 # with the implementation of ConfigParser. Using "format" instead.
574 key: value.format(**substitutions)
574 key: value.format(**substitutions)
575 for key, value in mapping.items()
575 for key, value in mapping.items()
576 }
576 }
577 except KeyError as e:
577 except KeyError as e:
578 raise ValueError(
578 raise ValueError(
579 'Failed to substitute env variable: {}. '
579 'Failed to substitute env variable: {}. '
580 'Make sure you have specified this env variable without ENV_ prefix'.format(e))
580 'Make sure you have specified this env variable without ENV_ prefix'.format(e))
581
581
582 return result
582 return result
General Comments 0
You need to be logged in to leave comments. Login now