##// END OF EJS Templates
middleware: use explicit call to .includeme while importing apps and modules. This is a bit faster for code discovery and app startup
super-admin -
r5024:cc92b68c default
parent child Browse files
Show More
@@ -1,612 +1,617 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 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 collections
23 import collections
24 import tempfile
24 import tempfile
25 import time
25 import time
26 import logging.config
26 import logging.config
27
27
28 from paste.gzipper import make_gzip_middleware
28 from paste.gzipper import make_gzip_middleware
29 import pyramid.events
29 import pyramid.events
30 from pyramid.wsgi import wsgiapp
30 from pyramid.wsgi import wsgiapp
31 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.authorization import ACLAuthorizationPolicy
32 from pyramid.config import Configurator
32 from pyramid.config import Configurator
33 from pyramid.settings import asbool, aslist
33 from pyramid.settings import asbool, aslist
34 from pyramid.httpexceptions import (
34 from pyramid.httpexceptions import (
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
36 from pyramid.renderers import render_to_response
36 from pyramid.renderers import render_to_response
37
37
38 from rhodecode.model import meta
38 from rhodecode.model import meta
39 from rhodecode.config import patches
39 from rhodecode.config import patches
40 from rhodecode.config import utils as config_utils
40 from rhodecode.config import utils as config_utils
41 from rhodecode.config.settings_maker import SettingsMaker
41 from rhodecode.config.settings_maker import SettingsMaker
42 from rhodecode.config.environment import load_pyramid_environment
42 from rhodecode.config.environment import load_pyramid_environment
43
43
44 import rhodecode.events
44 import rhodecode.events
45 from rhodecode.lib.middleware.vcs import VCSMiddleware
45 from rhodecode.lib.middleware.vcs import VCSMiddleware
46 from rhodecode.lib.request import Request
46 from rhodecode.lib.request import Request
47 from rhodecode.lib.vcs import VCSCommunicationError
47 from rhodecode.lib.vcs import VCSCommunicationError
48 from rhodecode.lib.exceptions import VCSServerUnavailable
48 from rhodecode.lib.exceptions import VCSServerUnavailable
49 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
50 from rhodecode.lib.middleware.https_fixup import HttpsFixup
50 from rhodecode.lib.middleware.https_fixup import HttpsFixup
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
52 from rhodecode.lib.utils2 import AttributeDict
52 from rhodecode.lib.utils2 import AttributeDict
53 from rhodecode.lib.exc_tracking import store_exception
53 from rhodecode.lib.exc_tracking import store_exception
54 from rhodecode.subscribers import (
54 from rhodecode.subscribers import (
55 scan_repositories_if_enabled, write_js_routes_if_enabled,
55 scan_repositories_if_enabled, write_js_routes_if_enabled,
56 write_metadata_if_needed, write_usage_data)
56 write_metadata_if_needed, write_usage_data)
57 from rhodecode.lib.statsd_client import StatsdClient
57 from rhodecode.lib.statsd_client import StatsdClient
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 def is_http_error(response):
62 def is_http_error(response):
63 # error which should have traceback
63 # error which should have traceback
64 return response.status_code > 499
64 return response.status_code > 499
65
65
66
66
67 def should_load_all():
67 def should_load_all():
68 """
68 """
69 Returns if all application components should be loaded. In some cases it's
69 Returns if all application components should be loaded. In some cases it's
70 desired to skip apps loading for faster shell script execution
70 desired to skip apps loading for faster shell script execution
71 """
71 """
72 ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER')
72 ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER')
73 if ssh_cmd:
73 if ssh_cmd:
74 return False
74 return False
75
75
76 return True
76 return True
77
77
78
78
79 def make_pyramid_app(global_config, **settings):
79 def make_pyramid_app(global_config, **settings):
80 """
80 """
81 Constructs the WSGI application based on Pyramid.
81 Constructs the WSGI application based on Pyramid.
82
82
83 Specials:
83 Specials:
84
84
85 * The application can also be integrated like a plugin via the call to
85 * The application can also be integrated like a plugin via the call to
86 `includeme`. This is accompanied with the other utility functions which
86 `includeme`. This is accompanied with the other utility functions which
87 are called. Changing this should be done with great care to not break
87 are called. Changing this should be done with great care to not break
88 cases when these fragments are assembled from another place.
88 cases when these fragments are assembled from another place.
89
89
90 """
90 """
91 start_time = time.time()
91 start_time = time.time()
92 log.info('Pyramid app config starting')
92 log.info('Pyramid app config starting')
93
93
94 sanitize_settings_and_apply_defaults(global_config, settings)
94 sanitize_settings_and_apply_defaults(global_config, settings)
95
95
96 # init and bootstrap StatsdClient
96 # init and bootstrap StatsdClient
97 StatsdClient.setup(settings)
97 StatsdClient.setup(settings)
98
98
99 config = Configurator(settings=settings)
99 config = Configurator(settings=settings)
100 # Init our statsd at very start
100 # Init our statsd at very start
101 config.registry.statsd = StatsdClient.statsd
101 config.registry.statsd = StatsdClient.statsd
102
102
103 # Apply compatibility patches
103 # Apply compatibility patches
104 patches.inspect_getargspec()
104 patches.inspect_getargspec()
105
105
106 load_pyramid_environment(global_config, settings)
106 load_pyramid_environment(global_config, settings)
107
107
108 # Static file view comes first
108 # Static file view comes first
109 includeme_first(config)
109 includeme_first(config)
110
110
111 includeme(config)
111 includeme(config)
112
112
113 pyramid_app = config.make_wsgi_app()
113 pyramid_app = config.make_wsgi_app()
114 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
114 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
115 pyramid_app.config = config
115 pyramid_app.config = config
116
116
117 celery_settings = get_celery_config(settings)
117 celery_settings = get_celery_config(settings)
118 config.configure_celery(celery_settings)
118 config.configure_celery(celery_settings)
119
119
120 # creating the app uses a connection - return it after we are done
120 # creating the app uses a connection - return it after we are done
121 meta.Session.remove()
121 meta.Session.remove()
122
122
123 total_time = time.time() - start_time
123 total_time = time.time() - start_time
124 log.info('Pyramid app `%s` created and configured in %.2fs',
124 log.info('Pyramid app `%s` created and configured in %.2fs',
125 getattr(pyramid_app, 'func_name', 'pyramid_app'), total_time)
125 getattr(pyramid_app, 'func_name', 'pyramid_app'), total_time)
126 return pyramid_app
126 return pyramid_app
127
127
128
128
129 def get_celery_config(settings):
129 def get_celery_config(settings):
130 """
130 """
131 Converts basic ini configuration into celery 4.X options
131 Converts basic ini configuration into celery 4.X options
132 """
132 """
133
133
134 def key_converter(key_name):
134 def key_converter(key_name):
135 pref = 'celery.'
135 pref = 'celery.'
136 if key_name.startswith(pref):
136 if key_name.startswith(pref):
137 return key_name[len(pref):].replace('.', '_').lower()
137 return key_name[len(pref):].replace('.', '_').lower()
138
138
139 def type_converter(parsed_key, value):
139 def type_converter(parsed_key, value):
140 # cast to int
140 # cast to int
141 if value.isdigit():
141 if value.isdigit():
142 return int(value)
142 return int(value)
143
143
144 # cast to bool
144 # cast to bool
145 if value.lower() in ['true', 'false', 'True', 'False']:
145 if value.lower() in ['true', 'false', 'True', 'False']:
146 return value.lower() == 'true'
146 return value.lower() == 'true'
147 return value
147 return value
148
148
149 celery_config = {}
149 celery_config = {}
150 for k, v in settings.items():
150 for k, v in settings.items():
151 pref = 'celery.'
151 pref = 'celery.'
152 if k.startswith(pref):
152 if k.startswith(pref):
153 celery_config[key_converter(k)] = type_converter(key_converter(k), v)
153 celery_config[key_converter(k)] = type_converter(key_converter(k), v)
154
154
155 # TODO:rethink if we want to support celerybeat based file config, probably NOT
155 # TODO:rethink if we want to support celerybeat based file config, probably NOT
156 # beat_config = {}
156 # beat_config = {}
157 # for section in parser.sections():
157 # for section in parser.sections():
158 # if section.startswith('celerybeat:'):
158 # if section.startswith('celerybeat:'):
159 # name = section.split(':', 1)[1]
159 # name = section.split(':', 1)[1]
160 # beat_config[name] = get_beat_config(parser, section)
160 # beat_config[name] = get_beat_config(parser, section)
161
161
162 # final compose of settings
162 # final compose of settings
163 celery_settings = {}
163 celery_settings = {}
164
164
165 if celery_config:
165 if celery_config:
166 celery_settings.update(celery_config)
166 celery_settings.update(celery_config)
167 # if beat_config:
167 # if beat_config:
168 # celery_settings.update({'beat_schedule': beat_config})
168 # celery_settings.update({'beat_schedule': beat_config})
169
169
170 return celery_settings
170 return celery_settings
171
171
172
172
173 def not_found_view(request):
173 def not_found_view(request):
174 """
174 """
175 This creates the view which should be registered as not-found-view to
175 This creates the view which should be registered as not-found-view to
176 pyramid.
176 pyramid.
177 """
177 """
178
178
179 if not getattr(request, 'vcs_call', None):
179 if not getattr(request, 'vcs_call', None):
180 # handle like regular case with our error_handler
180 # handle like regular case with our error_handler
181 return error_handler(HTTPNotFound(), request)
181 return error_handler(HTTPNotFound(), request)
182
182
183 # handle not found view as a vcs call
183 # handle not found view as a vcs call
184 settings = request.registry.settings
184 settings = request.registry.settings
185 ae_client = getattr(request, 'ae_client', None)
185 ae_client = getattr(request, 'ae_client', None)
186 vcs_app = VCSMiddleware(
186 vcs_app = VCSMiddleware(
187 HTTPNotFound(), request.registry, settings,
187 HTTPNotFound(), request.registry, settings,
188 appenlight_client=ae_client)
188 appenlight_client=ae_client)
189
189
190 return wsgiapp(vcs_app)(None, request)
190 return wsgiapp(vcs_app)(None, request)
191
191
192
192
193 def error_handler(exception, request):
193 def error_handler(exception, request):
194 import rhodecode
194 import rhodecode
195 from rhodecode.lib import helpers
195 from rhodecode.lib import helpers
196 from rhodecode.lib.utils2 import str2bool
196 from rhodecode.lib.utils2 import str2bool
197
197
198 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
198 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
199
199
200 base_response = HTTPInternalServerError()
200 base_response = HTTPInternalServerError()
201 # prefer original exception for the response since it may have headers set
201 # prefer original exception for the response since it may have headers set
202 if isinstance(exception, HTTPException):
202 if isinstance(exception, HTTPException):
203 base_response = exception
203 base_response = exception
204 elif isinstance(exception, VCSCommunicationError):
204 elif isinstance(exception, VCSCommunicationError):
205 base_response = VCSServerUnavailable()
205 base_response = VCSServerUnavailable()
206
206
207 if is_http_error(base_response):
207 if is_http_error(base_response):
208 log.exception(
208 log.exception(
209 'error occurred handling this request for path: %s', request.path)
209 'error occurred handling this request for path: %s', request.path)
210
210
211 error_explanation = base_response.explanation or str(base_response)
211 error_explanation = base_response.explanation or str(base_response)
212 if base_response.status_code == 404:
212 if base_response.status_code == 404:
213 error_explanation += " Optionally you don't have permission to access this page."
213 error_explanation += " Optionally you don't have permission to access this page."
214 c = AttributeDict()
214 c = AttributeDict()
215 c.error_message = base_response.status
215 c.error_message = base_response.status
216 c.error_explanation = error_explanation
216 c.error_explanation = error_explanation
217 c.visual = AttributeDict()
217 c.visual = AttributeDict()
218
218
219 c.visual.rhodecode_support_url = (
219 c.visual.rhodecode_support_url = (
220 request.registry.settings.get('rhodecode_support_url') or
220 request.registry.settings.get('rhodecode_support_url') or
221 request.route_url('rhodecode_support')
221 request.route_url('rhodecode_support')
222 )
222 )
223 c.redirect_time = 0
223 c.redirect_time = 0
224 c.rhodecode_name = rhodecode_title
224 c.rhodecode_name = rhodecode_title
225 if not c.rhodecode_name:
225 if not c.rhodecode_name:
226 c.rhodecode_name = 'Rhodecode'
226 c.rhodecode_name = 'Rhodecode'
227
227
228 c.causes = []
228 c.causes = []
229 if is_http_error(base_response):
229 if is_http_error(base_response):
230 c.causes.append('Server is overloaded.')
230 c.causes.append('Server is overloaded.')
231 c.causes.append('Server database connection is lost.')
231 c.causes.append('Server database connection is lost.')
232 c.causes.append('Server expected unhandled error.')
232 c.causes.append('Server expected unhandled error.')
233
233
234 if hasattr(base_response, 'causes'):
234 if hasattr(base_response, 'causes'):
235 c.causes = base_response.causes
235 c.causes = base_response.causes
236
236
237 c.messages = helpers.flash.pop_messages(request=request)
237 c.messages = helpers.flash.pop_messages(request=request)
238
239 exc_info = sys.exc_info()
238 exc_info = sys.exc_info()
240 c.exception_id = id(exc_info)
239 c.exception_id = id(exc_info)
241 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
240 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
242 or base_response.status_code > 499
241 or base_response.status_code > 499
243 c.exception_id_url = request.route_url(
242 c.exception_id_url = request.route_url(
244 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
243 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
245
244
246 if c.show_exception_id:
245 if c.show_exception_id:
247 store_exception(c.exception_id, exc_info)
246 store_exception(c.exception_id, exc_info)
248 c.exception_debug = str2bool(rhodecode.CONFIG.get('debug'))
247 c.exception_debug = str2bool(rhodecode.CONFIG.get('debug'))
249 c.exception_config_ini = rhodecode.CONFIG.get('__file__')
248 c.exception_config_ini = rhodecode.CONFIG.get('__file__')
250
249
251 response = render_to_response(
250 response = render_to_response(
252 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
251 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
253 response=base_response)
252 response=base_response)
254
253
255 statsd = request.registry.statsd
254 statsd = request.registry.statsd
256 if statsd and base_response.status_code > 499:
255 if statsd and base_response.status_code > 499:
257 exc_type = "{}.{}".format(exception.__class__.__module__, exception.__class__.__name__)
256 exc_type = "{}.{}".format(exception.__class__.__module__, exception.__class__.__name__)
258 statsd.incr('rhodecode_exception_total',
257 statsd.incr('rhodecode_exception_total',
259 tags=["exc_source:web",
258 tags=["exc_source:web",
260 "http_code:{}".format(base_response.status_code),
259 "http_code:{}".format(base_response.status_code),
261 "type:{}".format(exc_type)])
260 "type:{}".format(exc_type)])
262
261
263 return response
262 return response
264
263
265
264
266 def includeme_first(config):
265 def includeme_first(config):
267 # redirect automatic browser favicon.ico requests to correct place
266 # redirect automatic browser favicon.ico requests to correct place
268 def favicon_redirect(context, request):
267 def favicon_redirect(context, request):
269 return HTTPFound(
268 return HTTPFound(
270 request.static_path('rhodecode:public/images/favicon.ico'))
269 request.static_path('rhodecode:public/images/favicon.ico'))
271
270
272 config.add_view(favicon_redirect, route_name='favicon')
271 config.add_view(favicon_redirect, route_name='favicon')
273 config.add_route('favicon', '/favicon.ico')
272 config.add_route('favicon', '/favicon.ico')
274
273
275 def robots_redirect(context, request):
274 def robots_redirect(context, request):
276 return HTTPFound(
275 return HTTPFound(
277 request.static_path('rhodecode:public/robots.txt'))
276 request.static_path('rhodecode:public/robots.txt'))
278
277
279 config.add_view(robots_redirect, route_name='robots')
278 config.add_view(robots_redirect, route_name='robots')
280 config.add_route('robots', '/robots.txt')
279 config.add_route('robots', '/robots.txt')
281
280
282 config.add_static_view(
281 config.add_static_view(
283 '_static/deform', 'deform:static')
282 '_static/deform', 'deform:static')
284 config.add_static_view(
283 config.add_static_view(
285 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
284 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
286
285
287
286
288 def includeme(config, auth_resources=None):
287 def includeme(config, auth_resources=None):
289 from rhodecode.lib.celerylib.loader import configure_celery
288 from rhodecode.lib.celerylib.loader import configure_celery
290 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
289 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
291 settings = config.registry.settings
290 settings = config.registry.settings
292 config.set_request_factory(Request)
291 config.set_request_factory(Request)
293
292
294 # plugin information
293 # plugin information
295 config.registry.rhodecode_plugins = collections.OrderedDict()
294 config.registry.rhodecode_plugins = collections.OrderedDict()
296
295
297 config.add_directive(
296 config.add_directive(
298 'register_rhodecode_plugin', register_rhodecode_plugin)
297 'register_rhodecode_plugin', register_rhodecode_plugin)
299
298
300 config.add_directive('configure_celery', configure_celery)
299 config.add_directive('configure_celery', configure_celery)
301
300
302 if settings.get('appenlight', False):
301 if settings.get('appenlight', False):
303 config.include('appenlight_client.ext.pyramid_tween')
302 config.include('appenlight_client.ext.pyramid_tween')
304
303
305 load_all = should_load_all()
304 load_all = should_load_all()
306
305
307 # Includes which are required. The application would fail without them.
306 # Includes which are required. The application would fail without them.
308 config.include('pyramid_mako')
307 config.include('pyramid_mako')
309 config.include('rhodecode.lib.rc_beaker')
308 config.include('rhodecode.lib.rc_beaker')
310 config.include('rhodecode.lib.rc_cache')
309 config.include('rhodecode.lib.rc_cache')
311 config.include('rhodecode.apps._base.navigation')
310 config.include('rhodecode.apps._base.navigation')
312 config.include('rhodecode.apps._base.subscribers')
311 config.include('rhodecode.apps._base.subscribers')
313 config.include('rhodecode.tweens')
312 config.include('rhodecode.tweens')
314 config.include('rhodecode.authentication')
313 config.include('rhodecode.authentication')
315
314
316 if load_all:
315 if load_all:
317 ce_auth_resources = [
316 ce_auth_resources = [
318 'rhodecode.authentication.plugins.auth_crowd',
317 'rhodecode.authentication.plugins.auth_crowd',
319 'rhodecode.authentication.plugins.auth_headers',
318 'rhodecode.authentication.plugins.auth_headers',
320 'rhodecode.authentication.plugins.auth_jasig_cas',
319 'rhodecode.authentication.plugins.auth_jasig_cas',
321 'rhodecode.authentication.plugins.auth_ldap',
320 'rhodecode.authentication.plugins.auth_ldap',
322 'rhodecode.authentication.plugins.auth_pam',
321 'rhodecode.authentication.plugins.auth_pam',
323 'rhodecode.authentication.plugins.auth_rhodecode',
322 'rhodecode.authentication.plugins.auth_rhodecode',
324 'rhodecode.authentication.plugins.auth_token',
323 'rhodecode.authentication.plugins.auth_token',
325 ]
324 ]
326
325
327 # load CE authentication plugins
326 # load CE authentication plugins
328
327
329 if auth_resources:
328 if auth_resources:
330 ce_auth_resources.extend(auth_resources)
329 ce_auth_resources.extend(auth_resources)
331
330
332 for resource in ce_auth_resources:
331 for resource in ce_auth_resources:
333 config.include(resource)
332 config.include(resource)
334
333
335 # Auto discover authentication plugins and include their configuration.
334 # Auto discover authentication plugins and include their configuration.
336 if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')):
335 if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')):
337 from rhodecode.authentication import discover_legacy_plugins
336 from rhodecode.authentication import discover_legacy_plugins
338 discover_legacy_plugins(config)
337 discover_legacy_plugins(config)
339
338
340 # apps
339 # apps
341 if load_all:
340 if load_all:
342 config.include('rhodecode.api')
341 log.debug('Starting config.include() calls')
343 config.include('rhodecode.apps._base')
342 config.include('rhodecode.api.includeme')
344 config.include('rhodecode.apps.hovercards')
343 config.include('rhodecode.apps._base.includeme')
345 config.include('rhodecode.apps.ops')
344 config.include('rhodecode.apps._base.navigation.includeme')
346 config.include('rhodecode.apps.channelstream')
345 config.include('rhodecode.apps._base.subscribers.includeme')
347 config.include('rhodecode.apps.file_store')
346 config.include('rhodecode.apps.hovercards.includeme')
348 config.include('rhodecode.apps.admin')
347 config.include('rhodecode.apps.ops.includeme')
349 config.include('rhodecode.apps.login')
348 config.include('rhodecode.apps.channelstream.includeme')
350 config.include('rhodecode.apps.home')
349 config.include('rhodecode.apps.file_store.includeme')
351 config.include('rhodecode.apps.journal')
350 config.include('rhodecode.apps.admin.includeme')
351 config.include('rhodecode.apps.login.includeme')
352 config.include('rhodecode.apps.home.includeme')
353 config.include('rhodecode.apps.journal.includeme')
352
354
353 config.include('rhodecode.apps.repository')
355 config.include('rhodecode.apps.repository.includeme')
354 config.include('rhodecode.apps.repo_group')
356 config.include('rhodecode.apps.repo_group.includeme')
355 config.include('rhodecode.apps.user_group')
357 config.include('rhodecode.apps.user_group.includeme')
356 config.include('rhodecode.apps.search')
358 config.include('rhodecode.apps.search.includeme')
357 config.include('rhodecode.apps.user_profile')
359 config.include('rhodecode.apps.user_profile.includeme')
358 config.include('rhodecode.apps.user_group_profile')
360 config.include('rhodecode.apps.user_group_profile.includeme')
359 config.include('rhodecode.apps.my_account')
361 config.include('rhodecode.apps.my_account.includeme')
360 config.include('rhodecode.apps.gist')
362 config.include('rhodecode.apps.gist.includeme')
361
363
362 config.include('rhodecode.apps.svn_support')
364 config.include('rhodecode.apps.svn_support.includeme')
363 config.include('rhodecode.apps.ssh_support')
365 config.include('rhodecode.apps.ssh_support.includeme')
364 config.include('rhodecode.apps.debug_style')
366 config.include('rhodecode.apps.debug_style')
365
367
366 if load_all:
368 if load_all:
367 config.include('rhodecode.integrations')
369 config.include('rhodecode.integrations.includeme')
370 config.include('rhodecode.integrations.routes.includeme')
368
371
369 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
372 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
370 settings['default_locale_name'] = settings.get('lang', 'en')
373 settings['default_locale_name'] = settings.get('lang', 'en')
371 config.add_translation_dirs('rhodecode:i18n/')
374 config.add_translation_dirs('rhodecode:i18n/')
372
375
373 # Add subscribers.
376 # Add subscribers.
374 if load_all:
377 if load_all:
378 log.debug('Adding subscribers....')
375 config.add_subscriber(scan_repositories_if_enabled,
379 config.add_subscriber(scan_repositories_if_enabled,
376 pyramid.events.ApplicationCreated)
380 pyramid.events.ApplicationCreated)
377 config.add_subscriber(write_metadata_if_needed,
381 config.add_subscriber(write_metadata_if_needed,
378 pyramid.events.ApplicationCreated)
382 pyramid.events.ApplicationCreated)
379 config.add_subscriber(write_usage_data,
383 config.add_subscriber(write_usage_data,
380 pyramid.events.ApplicationCreated)
384 pyramid.events.ApplicationCreated)
381 config.add_subscriber(write_js_routes_if_enabled,
385 config.add_subscriber(write_js_routes_if_enabled,
382 pyramid.events.ApplicationCreated)
386 pyramid.events.ApplicationCreated)
383
387
384 # Set the authorization policy.
388 # Set the authorization policy.
385 authz_policy = ACLAuthorizationPolicy()
389 authz_policy = ACLAuthorizationPolicy()
386 config.set_authorization_policy(authz_policy)
390 config.set_authorization_policy(authz_policy)
387
391
388 # Set the default renderer for HTML templates to mako.
392 # Set the default renderer for HTML templates to mako.
389 config.add_mako_renderer('.html')
393 config.add_mako_renderer('.html')
390
394
391 config.add_renderer(
395 config.add_renderer(
392 name='json_ext',
396 name='json_ext',
393 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
397 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
394
398
395 config.add_renderer(
399 config.add_renderer(
396 name='string_html',
400 name='string_html',
397 factory='rhodecode.lib.string_renderer.html')
401 factory='rhodecode.lib.string_renderer.html')
398
402
399 # include RhodeCode plugins
403 # include RhodeCode plugins
400 includes = aslist(settings.get('rhodecode.includes', []))
404 includes = aslist(settings.get('rhodecode.includes', []))
405 log.debug('processing rhodecode.includes data...')
401 for inc in includes:
406 for inc in includes:
402 config.include(inc)
407 config.include(inc)
403
408
404 # custom not found view, if our pyramid app doesn't know how to handle
409 # custom not found view, if our pyramid app doesn't know how to handle
405 # the request pass it to potential VCS handling ap
410 # the request pass it to potential VCS handling ap
406 config.add_notfound_view(not_found_view)
411 config.add_notfound_view(not_found_view)
407 if not settings.get('debugtoolbar.enabled', False):
412 if not settings.get('debugtoolbar.enabled', False):
408 # disabled debugtoolbar handle all exceptions via the error_handlers
413 # disabled debugtoolbar handle all exceptions via the error_handlers
409 config.add_view(error_handler, context=Exception)
414 config.add_view(error_handler, context=Exception)
410
415
411 # all errors including 403/404/50X
416 # all errors including 403/404/50X
412 config.add_view(error_handler, context=HTTPError)
417 config.add_view(error_handler, context=HTTPError)
413
418
414
419
415 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
420 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
416 """
421 """
417 Apply outer WSGI middlewares around the application.
422 Apply outer WSGI middlewares around the application.
418 """
423 """
419 registry = config.registry
424 registry = config.registry
420 settings = registry.settings
425 settings = registry.settings
421
426
422 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
427 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
423 pyramid_app = HttpsFixup(pyramid_app, settings)
428 pyramid_app = HttpsFixup(pyramid_app, settings)
424
429
425 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
430 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
426 pyramid_app, settings)
431 pyramid_app, settings)
427 registry.ae_client = _ae_client
432 registry.ae_client = _ae_client
428
433
429 if settings['gzip_responses']:
434 if settings['gzip_responses']:
430 pyramid_app = make_gzip_middleware(
435 pyramid_app = make_gzip_middleware(
431 pyramid_app, settings, compress_level=1)
436 pyramid_app, settings, compress_level=1)
432
437
433 # this should be the outer most middleware in the wsgi stack since
438 # this should be the outer most middleware in the wsgi stack since
434 # middleware like Routes make database calls
439 # middleware like Routes make database calls
435 def pyramid_app_with_cleanup(environ, start_response):
440 def pyramid_app_with_cleanup(environ, start_response):
436 start = time.time()
441 start = time.time()
437 try:
442 try:
438 return pyramid_app(environ, start_response)
443 return pyramid_app(environ, start_response)
439 finally:
444 finally:
440 # Dispose current database session and rollback uncommitted
445 # Dispose current database session and rollback uncommitted
441 # transactions.
446 # transactions.
442 meta.Session.remove()
447 meta.Session.remove()
443
448
444 # In a single threaded mode server, on non sqlite db we should have
449 # In a single threaded mode server, on non sqlite db we should have
445 # '0 Current Checked out connections' at the end of a request,
450 # '0 Current Checked out connections' at the end of a request,
446 # if not, then something, somewhere is leaving a connection open
451 # if not, then something, somewhere is leaving a connection open
447 pool = meta.Base.metadata.bind.engine.pool
452 pool = meta.Base.metadata.bind.engine.pool
448 log.debug('sa pool status: %s', pool.status())
453 log.debug('sa pool status: %s', pool.status())
449 total = time.time() - start
454 total = time.time() - start
450 log.debug('Request processing finalized: %.4fs', total)
455 log.debug('Request processing finalized: %.4fs', total)
451
456
452 return pyramid_app_with_cleanup
457 return pyramid_app_with_cleanup
453
458
454
459
455 def sanitize_settings_and_apply_defaults(global_config, settings):
460 def sanitize_settings_and_apply_defaults(global_config, settings):
456 """
461 """
457 Applies settings defaults and does all type conversion.
462 Applies settings defaults and does all type conversion.
458
463
459 We would move all settings parsing and preparation into this place, so that
464 We would move all settings parsing and preparation into this place, so that
460 we have only one place left which deals with this part. The remaining parts
465 we have only one place left which deals with this part. The remaining parts
461 of the application would start to rely fully on well prepared settings.
466 of the application would start to rely fully on well prepared settings.
462
467
463 This piece would later be split up per topic to avoid a big fat monster
468 This piece would later be split up per topic to avoid a big fat monster
464 function.
469 function.
465 """
470 """
466
471
467 global_settings_maker = SettingsMaker(global_config)
472 global_settings_maker = SettingsMaker(global_config)
468 global_settings_maker.make_setting('debug', default=False, parser='bool')
473 global_settings_maker.make_setting('debug', default=False, parser='bool')
469 debug_enabled = asbool(global_config.get('debug'))
474 debug_enabled = asbool(global_config.get('debug'))
470
475
471 settings_maker = SettingsMaker(settings)
476 settings_maker = SettingsMaker(settings)
472
477
473 settings_maker.make_setting(
478 settings_maker.make_setting(
474 'logging.autoconfigure',
479 'logging.autoconfigure',
475 default=False,
480 default=False,
476 parser='bool')
481 parser='bool')
477
482
478 logging_conf = os.path.join(os.path.dirname(global_config.get('__file__')), 'logging.ini')
483 logging_conf = os.path.join(os.path.dirname(global_config.get('__file__')), 'logging.ini')
479 settings_maker.enable_logging(logging_conf, level='INFO' if debug_enabled else 'DEBUG')
484 settings_maker.enable_logging(logging_conf, level='INFO' if debug_enabled else 'DEBUG')
480
485
481 # Default includes, possible to change as a user
486 # Default includes, possible to change as a user
482 pyramid_includes = settings_maker.make_setting('pyramid.includes', [], parser='list:newline')
487 pyramid_includes = settings_maker.make_setting('pyramid.includes', [], parser='list:newline')
483 log.debug(
488 log.debug(
484 "Using the following pyramid.includes: %s",
489 "Using the following pyramid.includes: %s",
485 pyramid_includes)
490 pyramid_includes)
486
491
487 settings_maker.make_setting('rhodecode.edition', 'Community Edition')
492 settings_maker.make_setting('rhodecode.edition', 'Community Edition')
488 settings_maker.make_setting('rhodecode.edition_id', 'CE')
493 settings_maker.make_setting('rhodecode.edition_id', 'CE')
489
494
490 if 'mako.default_filters' not in settings:
495 if 'mako.default_filters' not in settings:
491 # set custom default filters if we don't have it defined
496 # set custom default filters if we don't have it defined
492 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
497 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
493 settings['mako.default_filters'] = 'h_filter'
498 settings['mako.default_filters'] = 'h_filter'
494
499
495 if 'mako.directories' not in settings:
500 if 'mako.directories' not in settings:
496 mako_directories = settings.setdefault('mako.directories', [
501 mako_directories = settings.setdefault('mako.directories', [
497 # Base templates of the original application
502 # Base templates of the original application
498 'rhodecode:templates',
503 'rhodecode:templates',
499 ])
504 ])
500 log.debug(
505 log.debug(
501 "Using the following Mako template directories: %s",
506 "Using the following Mako template directories: %s",
502 mako_directories)
507 mako_directories)
503
508
504 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
509 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
505 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
510 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
506 raw_url = settings['beaker.session.url']
511 raw_url = settings['beaker.session.url']
507 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
512 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
508 settings['beaker.session.url'] = 'redis://' + raw_url
513 settings['beaker.session.url'] = 'redis://' + raw_url
509
514
510 settings_maker.make_setting('__file__', global_config.get('__file__'))
515 settings_maker.make_setting('__file__', global_config.get('__file__'))
511
516
512 # TODO: johbo: Re-think this, usually the call to config.include
517 # TODO: johbo: Re-think this, usually the call to config.include
513 # should allow to pass in a prefix.
518 # should allow to pass in a prefix.
514 settings_maker.make_setting('rhodecode.api.url', '/_admin/api')
519 settings_maker.make_setting('rhodecode.api.url', '/_admin/api')
515
520
516 # Sanitize generic settings.
521 # Sanitize generic settings.
517 settings_maker.make_setting('default_encoding', 'UTF-8', parser='list')
522 settings_maker.make_setting('default_encoding', 'UTF-8', parser='list')
518 settings_maker.make_setting('is_test', False, parser='bool')
523 settings_maker.make_setting('is_test', False, parser='bool')
519 settings_maker.make_setting('gzip_responses', False, parser='bool')
524 settings_maker.make_setting('gzip_responses', False, parser='bool')
520
525
521 # statsd
526 # statsd
522 settings_maker.make_setting('statsd.enabled', False, parser='bool')
527 settings_maker.make_setting('statsd.enabled', False, parser='bool')
523 settings_maker.make_setting('statsd.statsd_host', 'statsd-exporter', parser='string')
528 settings_maker.make_setting('statsd.statsd_host', 'statsd-exporter', parser='string')
524 settings_maker.make_setting('statsd.statsd_port', 9125, parser='int')
529 settings_maker.make_setting('statsd.statsd_port', 9125, parser='int')
525 settings_maker.make_setting('statsd.statsd_prefix', '')
530 settings_maker.make_setting('statsd.statsd_prefix', '')
526 settings_maker.make_setting('statsd.statsd_ipv6', False, parser='bool')
531 settings_maker.make_setting('statsd.statsd_ipv6', False, parser='bool')
527
532
528 settings_maker.make_setting('vcs.svn.compatible_version', '')
533 settings_maker.make_setting('vcs.svn.compatible_version', '')
529 settings_maker.make_setting('vcs.hooks.protocol', 'http')
534 settings_maker.make_setting('vcs.hooks.protocol', 'http')
530 settings_maker.make_setting('vcs.hooks.host', '127.0.0.1')
535 settings_maker.make_setting('vcs.hooks.host', '127.0.0.1')
531 settings_maker.make_setting('vcs.scm_app_implementation', 'http')
536 settings_maker.make_setting('vcs.scm_app_implementation', 'http')
532 settings_maker.make_setting('vcs.server', '')
537 settings_maker.make_setting('vcs.server', '')
533 settings_maker.make_setting('vcs.server.protocol', 'http')
538 settings_maker.make_setting('vcs.server.protocol', 'http')
534 settings_maker.make_setting('vcs.server.enable', 'true', parser='bool')
539 settings_maker.make_setting('vcs.server.enable', 'true', parser='bool')
535 settings_maker.make_setting('startup.import_repos', 'false', parser='bool')
540 settings_maker.make_setting('startup.import_repos', 'false', parser='bool')
536 settings_maker.make_setting('vcs.hooks.direct_calls', 'false', parser='bool')
541 settings_maker.make_setting('vcs.hooks.direct_calls', 'false', parser='bool')
537 settings_maker.make_setting('vcs.start_server', 'false', parser='bool')
542 settings_maker.make_setting('vcs.start_server', 'false', parser='bool')
538 settings_maker.make_setting('vcs.backends', 'hg, git, svn', parser='list')
543 settings_maker.make_setting('vcs.backends', 'hg, git, svn', parser='list')
539 settings_maker.make_setting('vcs.connection_timeout', 3600, parser='int')
544 settings_maker.make_setting('vcs.connection_timeout', 3600, parser='int')
540
545
541 settings_maker.make_setting('vcs.methods.cache', True, parser='bool')
546 settings_maker.make_setting('vcs.methods.cache', True, parser='bool')
542
547
543 # Support legacy values of vcs.scm_app_implementation. Legacy
548 # Support legacy values of vcs.scm_app_implementation. Legacy
544 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
549 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
545 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
550 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
546 scm_app_impl = settings['vcs.scm_app_implementation']
551 scm_app_impl = settings['vcs.scm_app_implementation']
547 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
552 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
548 settings['vcs.scm_app_implementation'] = 'http'
553 settings['vcs.scm_app_implementation'] = 'http'
549
554
550 settings_maker.make_setting('appenlight', False, parser='bool')
555 settings_maker.make_setting('appenlight', False, parser='bool')
551
556
552 temp_store = tempfile.gettempdir()
557 temp_store = tempfile.gettempdir()
553 tmp_cache_dir = os.path.join(temp_store, 'rc_cache')
558 tmp_cache_dir = os.path.join(temp_store, 'rc_cache')
554
559
555 # save default, cache dir, and use it for all backends later.
560 # save default, cache dir, and use it for all backends later.
556 default_cache_dir = settings_maker.make_setting(
561 default_cache_dir = settings_maker.make_setting(
557 'cache_dir',
562 'cache_dir',
558 default=tmp_cache_dir, default_when_empty=True,
563 default=tmp_cache_dir, default_when_empty=True,
559 parser='dir:ensured')
564 parser='dir:ensured')
560
565
561 # exception store cache
566 # exception store cache
562 settings_maker.make_setting(
567 settings_maker.make_setting(
563 'exception_tracker.store_path',
568 'exception_tracker.store_path',
564 default=os.path.join(default_cache_dir, 'exc_store'), default_when_empty=True,
569 default=os.path.join(default_cache_dir, 'exc_store'), default_when_empty=True,
565 parser='dir:ensured'
570 parser='dir:ensured'
566 )
571 )
567
572
568 settings_maker.make_setting(
573 settings_maker.make_setting(
569 'celerybeat-schedule.path',
574 'celerybeat-schedule.path',
570 default=os.path.join(default_cache_dir, 'celerybeat_schedule', 'celerybeat-schedule.db'), default_when_empty=True,
575 default=os.path.join(default_cache_dir, 'celerybeat_schedule', 'celerybeat-schedule.db'), default_when_empty=True,
571 parser='file:ensured'
576 parser='file:ensured'
572 )
577 )
573
578
574 settings_maker.make_setting('exception_tracker.send_email', False, parser='bool')
579 settings_maker.make_setting('exception_tracker.send_email', False, parser='bool')
575 settings_maker.make_setting('exception_tracker.email_prefix', '[RHODECODE ERROR]', default_when_empty=True)
580 settings_maker.make_setting('exception_tracker.email_prefix', '[RHODECODE ERROR]', default_when_empty=True)
576
581
577 # cache_general
582 # cache_general
578 settings_maker.make_setting('rc_cache.cache_general.backend', 'dogpile.cache.rc.file_namespace')
583 settings_maker.make_setting('rc_cache.cache_general.backend', 'dogpile.cache.rc.file_namespace')
579 settings_maker.make_setting('rc_cache.cache_general.expiration_time', 60 * 60 * 12, parser='int')
584 settings_maker.make_setting('rc_cache.cache_general.expiration_time', 60 * 60 * 12, parser='int')
580 settings_maker.make_setting('rc_cache.cache_general.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_general.db'))
585 settings_maker.make_setting('rc_cache.cache_general.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_general.db'))
581
586
582 # cache_perms
587 # cache_perms
583 settings_maker.make_setting('rc_cache.cache_perms.backend', 'dogpile.cache.rc.file_namespace')
588 settings_maker.make_setting('rc_cache.cache_perms.backend', 'dogpile.cache.rc.file_namespace')
584 settings_maker.make_setting('rc_cache.cache_perms.expiration_time', 60 * 60, parser='int')
589 settings_maker.make_setting('rc_cache.cache_perms.expiration_time', 60 * 60, parser='int')
585 settings_maker.make_setting('rc_cache.cache_perms.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_perms.db'))
590 settings_maker.make_setting('rc_cache.cache_perms.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_perms.db'))
586
591
587 # cache_repo
592 # cache_repo
588 settings_maker.make_setting('rc_cache.cache_repo.backend', 'dogpile.cache.rc.file_namespace')
593 settings_maker.make_setting('rc_cache.cache_repo.backend', 'dogpile.cache.rc.file_namespace')
589 settings_maker.make_setting('rc_cache.cache_repo.expiration_time', 60 * 60 * 24 * 30, parser='int')
594 settings_maker.make_setting('rc_cache.cache_repo.expiration_time', 60 * 60 * 24 * 30, parser='int')
590 settings_maker.make_setting('rc_cache.cache_repo.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_repo.db'))
595 settings_maker.make_setting('rc_cache.cache_repo.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_repo.db'))
591
596
592 # cache_license
597 # cache_license
593 settings_maker.make_setting('rc_cache.cache_license.backend', 'dogpile.cache.rc.file_namespace')
598 settings_maker.make_setting('rc_cache.cache_license.backend', 'dogpile.cache.rc.file_namespace')
594 settings_maker.make_setting('rc_cache.cache_license.expiration_time', 60 * 5, parser='int')
599 settings_maker.make_setting('rc_cache.cache_license.expiration_time', 60 * 5, parser='int')
595 settings_maker.make_setting('rc_cache.cache_license.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_license.db'))
600 settings_maker.make_setting('rc_cache.cache_license.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_license.db'))
596
601
597 # cache_repo_longterm memory, 96H
602 # cache_repo_longterm memory, 96H
598 settings_maker.make_setting('rc_cache.cache_repo_longterm.backend', 'dogpile.cache.rc.memory_lru')
603 settings_maker.make_setting('rc_cache.cache_repo_longterm.backend', 'dogpile.cache.rc.memory_lru')
599 settings_maker.make_setting('rc_cache.cache_repo_longterm.expiration_time', 345600, parser='int')
604 settings_maker.make_setting('rc_cache.cache_repo_longterm.expiration_time', 345600, parser='int')
600 settings_maker.make_setting('rc_cache.cache_repo_longterm.max_size', 10000, parser='int')
605 settings_maker.make_setting('rc_cache.cache_repo_longterm.max_size', 10000, parser='int')
601
606
602 # sql_cache_short
607 # sql_cache_short
603 settings_maker.make_setting('rc_cache.sql_cache_short.backend', 'dogpile.cache.rc.memory_lru')
608 settings_maker.make_setting('rc_cache.sql_cache_short.backend', 'dogpile.cache.rc.memory_lru')
604 settings_maker.make_setting('rc_cache.sql_cache_short.expiration_time', 30, parser='int')
609 settings_maker.make_setting('rc_cache.sql_cache_short.expiration_time', 30, parser='int')
605 settings_maker.make_setting('rc_cache.sql_cache_short.max_size', 10000, parser='int')
610 settings_maker.make_setting('rc_cache.sql_cache_short.max_size', 10000, parser='int')
606
611
607 settings_maker.env_expand()
612 settings_maker.env_expand()
608
613
609 # configure instance id
614 # configure instance id
610 config_utils.set_instance_id(settings)
615 config_utils.set_instance_id(settings)
611
616
612 return settings
617 return settings
General Comments 0
You need to be logged in to leave comments. Login now