##// END OF EJS Templates
core: cleanup settings cleanups on pyramid app.
marcink -
r2316:41ee882b default
parent child Browse files
Show More
@@ -1,551 +1,542 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 import traceback
25 import traceback
26 from collections import OrderedDict
26 from collections import OrderedDict
27
27
28 from paste.registry import RegistryManager
28 from paste.registry import RegistryManager
29 from paste.gzipper import make_gzip_middleware
29 from paste.gzipper import make_gzip_middleware
30 from pylons.wsgiapp import PylonsApp
30 from pylons.wsgiapp import PylonsApp
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.wsgi import wsgiapp
34 from pyramid.wsgi import wsgiapp
35 from pyramid.httpexceptions import (
35 from pyramid.httpexceptions import (
36 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
36 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
37 from pyramid.events import ApplicationCreated
37 from pyramid.events import ApplicationCreated
38 from pyramid.renderers import render_to_response
38 from pyramid.renderers import render_to_response
39 from routes.middleware import RoutesMiddleware
39 from routes.middleware import RoutesMiddleware
40 import rhodecode
40 import rhodecode
41
41
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 import utils as config_utils
44 from rhodecode.config import utils as config_utils
45 from rhodecode.config.routing import STATIC_FILE_PREFIX
45 from rhodecode.config.routing import STATIC_FILE_PREFIX
46 from rhodecode.config.environment import (
46 from rhodecode.config.environment import (
47 load_environment, load_pyramid_environment)
47 load_environment, load_pyramid_environment)
48
48
49 from rhodecode.lib.vcs import VCSCommunicationError
49 from rhodecode.lib.vcs import VCSCommunicationError
50 from rhodecode.lib.exceptions import VCSServerUnavailable
50 from rhodecode.lib.exceptions import VCSServerUnavailable
51 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
51 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
52 from rhodecode.lib.middleware.error_handling import (
52 from rhodecode.lib.middleware.error_handling import (
53 PylonsErrorHandlingMiddleware)
53 PylonsErrorHandlingMiddleware)
54 from rhodecode.lib.middleware.https_fixup import HttpsFixup
54 from rhodecode.lib.middleware.https_fixup import HttpsFixup
55 from rhodecode.lib.middleware.vcs import VCSMiddleware
55 from rhodecode.lib.middleware.vcs import VCSMiddleware
56 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
56 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
57 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
57 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
58 from rhodecode.subscribers import (
58 from rhodecode.subscribers import (
59 scan_repositories_if_enabled, write_js_routes_if_enabled,
59 scan_repositories_if_enabled, write_js_routes_if_enabled,
60 write_metadata_if_needed, inject_app_settings)
60 write_metadata_if_needed, inject_app_settings)
61
61
62
62
63 log = logging.getLogger(__name__)
63 log = logging.getLogger(__name__)
64
64
65
65
66 # this is used to avoid avoid the route lookup overhead in routesmiddleware
66 # this is used to avoid avoid the route lookup overhead in routesmiddleware
67 # for certain routes which won't go to pylons to - eg. static files, debugger
67 # for certain routes which won't go to pylons to - eg. static files, debugger
68 # it is only needed for the pylons migration and can be removed once complete
68 # it is only needed for the pylons migration and can be removed once complete
69 class SkippableRoutesMiddleware(RoutesMiddleware):
69 class SkippableRoutesMiddleware(RoutesMiddleware):
70 """ Routes middleware that allows you to skip prefixes """
70 """ Routes middleware that allows you to skip prefixes """
71
71
72 def __init__(self, *args, **kw):
72 def __init__(self, *args, **kw):
73 self.skip_prefixes = kw.pop('skip_prefixes', [])
73 self.skip_prefixes = kw.pop('skip_prefixes', [])
74 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
74 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
75
75
76 def __call__(self, environ, start_response):
76 def __call__(self, environ, start_response):
77 for prefix in self.skip_prefixes:
77 for prefix in self.skip_prefixes:
78 if environ['PATH_INFO'].startswith(prefix):
78 if environ['PATH_INFO'].startswith(prefix):
79 # added to avoid the case when a missing /_static route falls
79 # added to avoid the case when a missing /_static route falls
80 # through to pylons and causes an exception as pylons is
80 # through to pylons and causes an exception as pylons is
81 # expecting wsgiorg.routingargs to be set in the environ
81 # expecting wsgiorg.routingargs to be set in the environ
82 # by RoutesMiddleware.
82 # by RoutesMiddleware.
83 if 'wsgiorg.routing_args' not in environ:
83 if 'wsgiorg.routing_args' not in environ:
84 environ['wsgiorg.routing_args'] = (None, {})
84 environ['wsgiorg.routing_args'] = (None, {})
85 return self.app(environ, start_response)
85 return self.app(environ, start_response)
86
86
87 return super(SkippableRoutesMiddleware, self).__call__(
87 return super(SkippableRoutesMiddleware, self).__call__(
88 environ, start_response)
88 environ, start_response)
89
89
90
90
91 def make_app(global_conf, static_files=True, **app_conf):
91 def make_app(global_conf, static_files=True, **app_conf):
92 """Create a Pylons WSGI application and return it
92 """Create a Pylons WSGI application and return it
93
93
94 ``global_conf``
94 ``global_conf``
95 The inherited configuration for this application. Normally from
95 The inherited configuration for this application. Normally from
96 the [DEFAULT] section of the Paste ini file.
96 the [DEFAULT] section of the Paste ini file.
97
97
98 ``app_conf``
98 ``app_conf``
99 The application's local configuration. Normally specified in
99 The application's local configuration. Normally specified in
100 the [app:<name>] section of the Paste ini file (where <name>
100 the [app:<name>] section of the Paste ini file (where <name>
101 defaults to main).
101 defaults to main).
102
102
103 """
103 """
104 # Apply compatibility patches
104 # Apply compatibility patches
105 patches.kombu_1_5_1_python_2_7_11()
105 patches.kombu_1_5_1_python_2_7_11()
106 patches.inspect_getargspec()
106 patches.inspect_getargspec()
107
107
108 # Configure the Pylons environment
108 # Configure the Pylons environment
109 config = load_environment(global_conf, app_conf)
109 config = load_environment(global_conf, app_conf)
110
110
111 # The Pylons WSGI app
111 # The Pylons WSGI app
112 app = PylonsApp(config=config)
112 app = PylonsApp(config=config)
113
113
114 # Establish the Registry for this application
114 # Establish the Registry for this application
115 app = RegistryManager(app)
115 app = RegistryManager(app)
116
116
117 app.config = config
117 app.config = config
118
118
119 return app
119 return app
120
120
121
121
122 def make_pyramid_app(global_config, **settings):
122 def make_pyramid_app(global_config, **settings):
123 """
123 """
124 Constructs the WSGI application based on Pyramid and wraps the Pylons based
124 Constructs the WSGI application based on Pyramid and wraps the Pylons based
125 application.
125 application.
126
126
127 Specials:
127 Specials:
128
128
129 * We migrate from Pylons to Pyramid. While doing this, we keep both
129 * We migrate from Pylons to Pyramid. While doing this, we keep both
130 frameworks functional. This involves moving some WSGI middlewares around
130 frameworks functional. This involves moving some WSGI middlewares around
131 and providing access to some data internals, so that the old code is
131 and providing access to some data internals, so that the old code is
132 still functional.
132 still functional.
133
133
134 * The application can also be integrated like a plugin via the call to
134 * The application can also be integrated like a plugin via the call to
135 `includeme`. This is accompanied with the other utility functions which
135 `includeme`. This is accompanied with the other utility functions which
136 are called. Changing this should be done with great care to not break
136 are called. Changing this should be done with great care to not break
137 cases when these fragments are assembled from another place.
137 cases when these fragments are assembled from another place.
138
138
139 """
139 """
140 # The edition string should be available in pylons too, so we add it here
141 # before copying the settings.
142 settings.setdefault('rhodecode.edition', 'Community Edition')
143
144 # As long as our Pylons application does expect "unprepared" settings, make
145 # sure that we keep an unmodified copy. This avoids unintentional change of
146 # behavior in the old application.
147 settings_pylons = settings.copy()
148
149 sanitize_settings_and_apply_defaults(settings)
140 sanitize_settings_and_apply_defaults(settings)
150
141
151 config = Configurator(settings=settings)
142 config = Configurator(settings=settings)
152 load_pyramid_environment(global_config, settings)
143 load_pyramid_environment(global_config, settings)
153
144
154 add_pylons_compat_data(config.registry, global_config, settings_pylons)
145 add_pylons_compat_data(config.registry, global_config, settings.copy())
155
146
156 includeme_first(config)
147 includeme_first(config)
157 includeme(config)
148 includeme(config)
158
149
159 pyramid_app = config.make_wsgi_app()
150 pyramid_app = config.make_wsgi_app()
160 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
151 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
161 pyramid_app.config = config
152 pyramid_app.config = config
162
153
163 # creating the app uses a connection - return it after we are done
154 # creating the app uses a connection - return it after we are done
164 meta.Session.remove()
155 meta.Session.remove()
165
156
166 return pyramid_app
157 return pyramid_app
167
158
168
159
169 def make_not_found_view(config):
160 def make_not_found_view(config):
170 """
161 """
171 This creates the view which should be registered as not-found-view to
162 This creates the view which should be registered as not-found-view to
172 pyramid. Basically it contains of the old pylons app, converted to a view.
163 pyramid. Basically it contains of the old pylons app, converted to a view.
173 Additionally it is wrapped by some other middlewares.
164 Additionally it is wrapped by some other middlewares.
174 """
165 """
175 settings = config.registry.settings
166 settings = config.registry.settings
176 vcs_server_enabled = settings['vcs.server.enable']
167 vcs_server_enabled = settings['vcs.server.enable']
177
168
178 # Make pylons app from unprepared settings.
169 # Make pylons app from unprepared settings.
179 pylons_app = make_app(
170 pylons_app = make_app(
180 config.registry._pylons_compat_global_config,
171 config.registry._pylons_compat_global_config,
181 **config.registry._pylons_compat_settings)
172 **config.registry._pylons_compat_settings)
182 config.registry._pylons_compat_config = pylons_app.config
173 config.registry._pylons_compat_config = pylons_app.config
183
174
184 # Appenlight monitoring.
175 # Appenlight monitoring.
185 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
176 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
186 pylons_app, settings)
177 pylons_app, settings)
187
178
188 # The pylons app is executed inside of the pyramid 404 exception handler.
179 # The pylons app is executed inside of the pyramid 404 exception handler.
189 # Exceptions which are raised inside of it are not handled by pyramid
180 # Exceptions which are raised inside of it are not handled by pyramid
190 # again. Therefore we add a middleware that invokes the error handler in
181 # again. Therefore we add a middleware that invokes the error handler in
191 # case of an exception or error response. This way we return proper error
182 # case of an exception or error response. This way we return proper error
192 # HTML pages in case of an error.
183 # HTML pages in case of an error.
193 reraise = (settings.get('debugtoolbar.enabled', False) or
184 reraise = (settings.get('debugtoolbar.enabled', False) or
194 rhodecode.disable_error_handler)
185 rhodecode.disable_error_handler)
195 pylons_app = PylonsErrorHandlingMiddleware(
186 pylons_app = PylonsErrorHandlingMiddleware(
196 pylons_app, error_handler, reraise)
187 pylons_app, error_handler, reraise)
197
188
198 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
189 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
199 # view to handle the request. Therefore it is wrapped around the pylons
190 # view to handle the request. Therefore it is wrapped around the pylons
200 # app. It has to be outside of the error handling otherwise error responses
191 # app. It has to be outside of the error handling otherwise error responses
201 # from the vcsserver are converted to HTML error pages. This confuses the
192 # from the vcsserver are converted to HTML error pages. This confuses the
202 # command line tools and the user won't get a meaningful error message.
193 # command line tools and the user won't get a meaningful error message.
203 if vcs_server_enabled:
194 if vcs_server_enabled:
204 pylons_app = VCSMiddleware(
195 pylons_app = VCSMiddleware(
205 pylons_app, settings, appenlight_client, registry=config.registry)
196 pylons_app, settings, appenlight_client, registry=config.registry)
206
197
207 # Convert WSGI app to pyramid view and return it.
198 # Convert WSGI app to pyramid view and return it.
208 return wsgiapp(pylons_app)
199 return wsgiapp(pylons_app)
209
200
210
201
211 def add_pylons_compat_data(registry, global_config, settings):
202 def add_pylons_compat_data(registry, global_config, settings):
212 """
203 """
213 Attach data to the registry to support the Pylons integration.
204 Attach data to the registry to support the Pylons integration.
214 """
205 """
215 registry._pylons_compat_global_config = global_config
206 registry._pylons_compat_global_config = global_config
216 registry._pylons_compat_settings = settings
207 registry._pylons_compat_settings = settings
217
208
218
209
219 def error_handler(exception, request):
210 def error_handler(exception, request):
220 import rhodecode
211 import rhodecode
221 from rhodecode.lib import helpers
212 from rhodecode.lib import helpers
222
213
223 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
214 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
224
215
225 base_response = HTTPInternalServerError()
216 base_response = HTTPInternalServerError()
226 # prefer original exception for the response since it may have headers set
217 # prefer original exception for the response since it may have headers set
227 if isinstance(exception, HTTPException):
218 if isinstance(exception, HTTPException):
228 base_response = exception
219 base_response = exception
229 elif isinstance(exception, VCSCommunicationError):
220 elif isinstance(exception, VCSCommunicationError):
230 base_response = VCSServerUnavailable()
221 base_response = VCSServerUnavailable()
231
222
232 def is_http_error(response):
223 def is_http_error(response):
233 # error which should have traceback
224 # error which should have traceback
234 return response.status_code > 499
225 return response.status_code > 499
235
226
236 if is_http_error(base_response):
227 if is_http_error(base_response):
237 log.exception(
228 log.exception(
238 'error occurred handling this request for path: %s', request.path)
229 'error occurred handling this request for path: %s', request.path)
239
230
240 error_explanation = base_response.explanation or str(base_response)
231 error_explanation = base_response.explanation or str(base_response)
241 if base_response.status_code == 404:
232 if base_response.status_code == 404:
242 error_explanation += " Or you don't have permission to access it."
233 error_explanation += " Or you don't have permission to access it."
243 c = AttributeDict()
234 c = AttributeDict()
244 c.error_message = base_response.status
235 c.error_message = base_response.status
245 c.error_explanation = error_explanation
236 c.error_explanation = error_explanation
246 c.visual = AttributeDict()
237 c.visual = AttributeDict()
247
238
248 c.visual.rhodecode_support_url = (
239 c.visual.rhodecode_support_url = (
249 request.registry.settings.get('rhodecode_support_url') or
240 request.registry.settings.get('rhodecode_support_url') or
250 request.route_url('rhodecode_support')
241 request.route_url('rhodecode_support')
251 )
242 )
252 c.redirect_time = 0
243 c.redirect_time = 0
253 c.rhodecode_name = rhodecode_title
244 c.rhodecode_name = rhodecode_title
254 if not c.rhodecode_name:
245 if not c.rhodecode_name:
255 c.rhodecode_name = 'Rhodecode'
246 c.rhodecode_name = 'Rhodecode'
256
247
257 c.causes = []
248 c.causes = []
258 if is_http_error(base_response):
249 if is_http_error(base_response):
259 c.causes.append('Server is overloaded.')
250 c.causes.append('Server is overloaded.')
260 c.causes.append('Server database connection is lost.')
251 c.causes.append('Server database connection is lost.')
261 c.causes.append('Server expected unhandled error.')
252 c.causes.append('Server expected unhandled error.')
262
253
263 if hasattr(base_response, 'causes'):
254 if hasattr(base_response, 'causes'):
264 c.causes = base_response.causes
255 c.causes = base_response.causes
265
256
266 c.messages = helpers.flash.pop_messages(request=request)
257 c.messages = helpers.flash.pop_messages(request=request)
267 c.traceback = traceback.format_exc()
258 c.traceback = traceback.format_exc()
268 response = render_to_response(
259 response = render_to_response(
269 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
260 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
270 response=base_response)
261 response=base_response)
271
262
272 return response
263 return response
273
264
274
265
275 def includeme(config):
266 def includeme(config):
276 settings = config.registry.settings
267 settings = config.registry.settings
277
268
278 # plugin information
269 # plugin information
279 config.registry.rhodecode_plugins = OrderedDict()
270 config.registry.rhodecode_plugins = OrderedDict()
280
271
281 config.add_directive(
272 config.add_directive(
282 'register_rhodecode_plugin', register_rhodecode_plugin)
273 'register_rhodecode_plugin', register_rhodecode_plugin)
283
274
284 if asbool(settings.get('appenlight', 'false')):
275 if asbool(settings.get('appenlight', 'false')):
285 config.include('appenlight_client.ext.pyramid_tween')
276 config.include('appenlight_client.ext.pyramid_tween')
286
277
287 if 'mako.default_filters' not in settings:
288 # set custom default filters if we don't have it defined
289 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
290 settings['mako.default_filters'] = 'h_filter'
291
292 # Includes which are required. The application would fail without them.
278 # Includes which are required. The application would fail without them.
293 config.include('pyramid_mako')
279 config.include('pyramid_mako')
294 config.include('pyramid_beaker')
280 config.include('pyramid_beaker')
295
281
296 config.include('rhodecode.authentication')
282 config.include('rhodecode.authentication')
297 config.include('rhodecode.integrations')
283 config.include('rhodecode.integrations')
298
284
299 # apps
285 # apps
300 config.include('rhodecode.apps._base')
286 config.include('rhodecode.apps._base')
301 config.include('rhodecode.apps.ops')
287 config.include('rhodecode.apps.ops')
302
288
303 config.include('rhodecode.apps.admin')
289 config.include('rhodecode.apps.admin')
304 config.include('rhodecode.apps.channelstream')
290 config.include('rhodecode.apps.channelstream')
305 config.include('rhodecode.apps.login')
291 config.include('rhodecode.apps.login')
306 config.include('rhodecode.apps.home')
292 config.include('rhodecode.apps.home')
307 config.include('rhodecode.apps.journal')
293 config.include('rhodecode.apps.journal')
308 config.include('rhodecode.apps.repository')
294 config.include('rhodecode.apps.repository')
309 config.include('rhodecode.apps.repo_group')
295 config.include('rhodecode.apps.repo_group')
310 config.include('rhodecode.apps.user_group')
296 config.include('rhodecode.apps.user_group')
311 config.include('rhodecode.apps.search')
297 config.include('rhodecode.apps.search')
312 config.include('rhodecode.apps.user_profile')
298 config.include('rhodecode.apps.user_profile')
313 config.include('rhodecode.apps.my_account')
299 config.include('rhodecode.apps.my_account')
314 config.include('rhodecode.apps.svn_support')
300 config.include('rhodecode.apps.svn_support')
315 config.include('rhodecode.apps.ssh_support')
301 config.include('rhodecode.apps.ssh_support')
316 config.include('rhodecode.apps.gist')
302 config.include('rhodecode.apps.gist')
317
303
318 config.include('rhodecode.apps.debug_style')
304 config.include('rhodecode.apps.debug_style')
319 config.include('rhodecode.tweens')
305 config.include('rhodecode.tweens')
320 config.include('rhodecode.api')
306 config.include('rhodecode.api')
321
307
322 config.add_route(
308 config.add_route(
323 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
309 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
324
310
325 config.add_translation_dirs('rhodecode:i18n/')
311 config.add_translation_dirs('rhodecode:i18n/')
326 settings['default_locale_name'] = settings.get('lang', 'en')
312 settings['default_locale_name'] = settings.get('lang', 'en')
327
313
328 # Add subscribers.
314 # Add subscribers.
329 config.add_subscriber(inject_app_settings, ApplicationCreated)
315 config.add_subscriber(inject_app_settings, ApplicationCreated)
330 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
316 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
331 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
317 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
332 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
318 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
333
319
334 config.add_request_method(
320 config.add_request_method(
335 'rhodecode.lib.partial_renderer.get_partial_renderer',
321 'rhodecode.lib.partial_renderer.get_partial_renderer',
336 'get_partial_renderer')
322 'get_partial_renderer')
337
323
338 # events
324 # events
339 # TODO(marcink): this should be done when pyramid migration is finished
325 # TODO(marcink): this should be done when pyramid migration is finished
340 # config.add_subscriber(
326 # config.add_subscriber(
341 # 'rhodecode.integrations.integrations_event_handler',
327 # 'rhodecode.integrations.integrations_event_handler',
342 # 'rhodecode.events.RhodecodeEvent')
328 # 'rhodecode.events.RhodecodeEvent')
343
329
344 # Set the authorization policy.
330 # Set the authorization policy.
345 authz_policy = ACLAuthorizationPolicy()
331 authz_policy = ACLAuthorizationPolicy()
346 config.set_authorization_policy(authz_policy)
332 config.set_authorization_policy(authz_policy)
347
333
348 # Set the default renderer for HTML templates to mako.
334 # Set the default renderer for HTML templates to mako.
349 config.add_mako_renderer('.html')
335 config.add_mako_renderer('.html')
350
336
351 config.add_renderer(
337 config.add_renderer(
352 name='json_ext',
338 name='json_ext',
353 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
339 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
354
340
355 # include RhodeCode plugins
341 # include RhodeCode plugins
356 includes = aslist(settings.get('rhodecode.includes', []))
342 includes = aslist(settings.get('rhodecode.includes', []))
357 for inc in includes:
343 for inc in includes:
358 config.include(inc)
344 config.include(inc)
359
345
360 # This is the glue which allows us to migrate in chunks. By registering the
346 # This is the glue which allows us to migrate in chunks. By registering the
361 # pylons based application as the "Not Found" view in Pyramid, we will
347 # pylons based application as the "Not Found" view in Pyramid, we will
362 # fallback to the old application each time the new one does not yet know
348 # fallback to the old application each time the new one does not yet know
363 # how to handle a request.
349 # how to handle a request.
364 config.add_notfound_view(make_not_found_view(config))
350 config.add_notfound_view(make_not_found_view(config))
365
351
366 if not settings.get('debugtoolbar.enabled', False):
352 if not settings.get('debugtoolbar.enabled', False):
367 # disabled debugtoolbar handle all exceptions via the error_handlers
353 # disabled debugtoolbar handle all exceptions via the error_handlers
368 config.add_view(error_handler, context=Exception)
354 config.add_view(error_handler, context=Exception)
369
355
370 config.add_view(error_handler, context=HTTPError)
356 config.add_view(error_handler, context=HTTPError)
371
357
372
358
373 def includeme_first(config):
359 def includeme_first(config):
374 # redirect automatic browser favicon.ico requests to correct place
360 # redirect automatic browser favicon.ico requests to correct place
375 def favicon_redirect(context, request):
361 def favicon_redirect(context, request):
376 return HTTPFound(
362 return HTTPFound(
377 request.static_path('rhodecode:public/images/favicon.ico'))
363 request.static_path('rhodecode:public/images/favicon.ico'))
378
364
379 config.add_view(favicon_redirect, route_name='favicon')
365 config.add_view(favicon_redirect, route_name='favicon')
380 config.add_route('favicon', '/favicon.ico')
366 config.add_route('favicon', '/favicon.ico')
381
367
382 def robots_redirect(context, request):
368 def robots_redirect(context, request):
383 return HTTPFound(
369 return HTTPFound(
384 request.static_path('rhodecode:public/robots.txt'))
370 request.static_path('rhodecode:public/robots.txt'))
385
371
386 config.add_view(robots_redirect, route_name='robots')
372 config.add_view(robots_redirect, route_name='robots')
387 config.add_route('robots', '/robots.txt')
373 config.add_route('robots', '/robots.txt')
388
374
389 config.add_static_view(
375 config.add_static_view(
390 '_static/deform', 'deform:static')
376 '_static/deform', 'deform:static')
391 config.add_static_view(
377 config.add_static_view(
392 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
378 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
393
379
394
380
395 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
381 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
396 """
382 """
397 Apply outer WSGI middlewares around the application.
383 Apply outer WSGI middlewares around the application.
398
384
399 Part of this has been moved up from the Pylons layer, so that the
385 Part of this has been moved up from the Pylons layer, so that the
400 data is also available if old Pylons code is hit through an already ported
386 data is also available if old Pylons code is hit through an already ported
401 view.
387 view.
402 """
388 """
403 settings = config.registry.settings
389 settings = config.registry.settings
404
390
405 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
391 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
406 pyramid_app = HttpsFixup(pyramid_app, settings)
392 pyramid_app = HttpsFixup(pyramid_app, settings)
407
393
408 # Add RoutesMiddleware to support the pylons compatibility tween during
394 # Add RoutesMiddleware to support the pylons compatibility tween during
409 # migration to pyramid.
395 # migration to pyramid.
410
396
411 # TODO(marcink): remove after migration to pyramid
397 # TODO(marcink): remove after migration to pyramid
412 if hasattr(config.registry, '_pylons_compat_config'):
398 if hasattr(config.registry, '_pylons_compat_config'):
413 routes_map = config.registry._pylons_compat_config['routes.map']
399 routes_map = config.registry._pylons_compat_config['routes.map']
414 pyramid_app = SkippableRoutesMiddleware(
400 pyramid_app = SkippableRoutesMiddleware(
415 pyramid_app, routes_map,
401 pyramid_app, routes_map,
416 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
402 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
417
403
418 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
404 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
419
405
420 if settings['gzip_responses']:
406 if settings['gzip_responses']:
421 pyramid_app = make_gzip_middleware(
407 pyramid_app = make_gzip_middleware(
422 pyramid_app, settings, compress_level=1)
408 pyramid_app, settings, compress_level=1)
423
409
424 # this should be the outer most middleware in the wsgi stack since
410 # this should be the outer most middleware in the wsgi stack since
425 # middleware like Routes make database calls
411 # middleware like Routes make database calls
426 def pyramid_app_with_cleanup(environ, start_response):
412 def pyramid_app_with_cleanup(environ, start_response):
427 try:
413 try:
428 return pyramid_app(environ, start_response)
414 return pyramid_app(environ, start_response)
429 finally:
415 finally:
430 # Dispose current database session and rollback uncommitted
416 # Dispose current database session and rollback uncommitted
431 # transactions.
417 # transactions.
432 meta.Session.remove()
418 meta.Session.remove()
433
419
434 # In a single threaded mode server, on non sqlite db we should have
420 # In a single threaded mode server, on non sqlite db we should have
435 # '0 Current Checked out connections' at the end of a request,
421 # '0 Current Checked out connections' at the end of a request,
436 # if not, then something, somewhere is leaving a connection open
422 # if not, then something, somewhere is leaving a connection open
437 pool = meta.Base.metadata.bind.engine.pool
423 pool = meta.Base.metadata.bind.engine.pool
438 log.debug('sa pool status: %s', pool.status())
424 log.debug('sa pool status: %s', pool.status())
439
425
440 return pyramid_app_with_cleanup
426 return pyramid_app_with_cleanup
441
427
442
428
443 def sanitize_settings_and_apply_defaults(settings):
429 def sanitize_settings_and_apply_defaults(settings):
444 """
430 """
445 Applies settings defaults and does all type conversion.
431 Applies settings defaults and does all type conversion.
446
432
447 We would move all settings parsing and preparation into this place, so that
433 We would move all settings parsing and preparation into this place, so that
448 we have only one place left which deals with this part. The remaining parts
434 we have only one place left which deals with this part. The remaining parts
449 of the application would start to rely fully on well prepared settings.
435 of the application would start to rely fully on well prepared settings.
450
436
451 This piece would later be split up per topic to avoid a big fat monster
437 This piece would later be split up per topic to avoid a big fat monster
452 function.
438 function.
453 """
439 """
454
440
455 # Pyramid's mako renderer has to search in the templates folder so that the
441 settings.setdefault('rhodecode.edition', 'Community Edition')
456 # old templates still work. Ported and new templates are expected to use
442
457 # real asset specifications for the includes.
443 if 'mako.default_filters' not in settings:
458 mako_directories = settings.setdefault('mako.directories', [
444 # set custom default filters if we don't have it defined
459 # Base templates of the original Pylons application
445 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
460 'rhodecode:templates',
446 settings['mako.default_filters'] = 'h_filter'
461 ])
447
462 log.debug(
448 if 'mako.directories' not in settings:
463 "Using the following Mako template directories: %s",
449 mako_directories = settings.setdefault('mako.directories', [
464 mako_directories)
450 # Base templates of the original application
451 'rhodecode:templates',
452 ])
453 log.debug(
454 "Using the following Mako template directories: %s",
455 mako_directories)
465
456
466 # Default includes, possible to change as a user
457 # Default includes, possible to change as a user
467 pyramid_includes = settings.setdefault('pyramid.includes', [
458 pyramid_includes = settings.setdefault('pyramid.includes', [
468 'rhodecode.lib.middleware.request_wrapper',
459 'rhodecode.lib.middleware.request_wrapper',
469 ])
460 ])
470 log.debug(
461 log.debug(
471 "Using the following pyramid.includes: %s",
462 "Using the following pyramid.includes: %s",
472 pyramid_includes)
463 pyramid_includes)
473
464
474 # TODO: johbo: Re-think this, usually the call to config.include
465 # TODO: johbo: Re-think this, usually the call to config.include
475 # should allow to pass in a prefix.
466 # should allow to pass in a prefix.
476 settings.setdefault('rhodecode.api.url', '/_admin/api')
467 settings.setdefault('rhodecode.api.url', '/_admin/api')
477
468
478 # Sanitize generic settings.
469 # Sanitize generic settings.
479 _list_setting(settings, 'default_encoding', 'UTF-8')
470 _list_setting(settings, 'default_encoding', 'UTF-8')
480 _bool_setting(settings, 'is_test', 'false')
471 _bool_setting(settings, 'is_test', 'false')
481 _bool_setting(settings, 'gzip_responses', 'false')
472 _bool_setting(settings, 'gzip_responses', 'false')
482
473
483 # Call split out functions that sanitize settings for each topic.
474 # Call split out functions that sanitize settings for each topic.
484 _sanitize_appenlight_settings(settings)
475 _sanitize_appenlight_settings(settings)
485 _sanitize_vcs_settings(settings)
476 _sanitize_vcs_settings(settings)
486
477
487 # configure instance id
478 # configure instance id
488 config_utils.set_instance_id(settings)
479 config_utils.set_instance_id(settings)
489
480
490 return settings
481 return settings
491
482
492
483
493 def _sanitize_appenlight_settings(settings):
484 def _sanitize_appenlight_settings(settings):
494 _bool_setting(settings, 'appenlight', 'false')
485 _bool_setting(settings, 'appenlight', 'false')
495
486
496
487
497 def _sanitize_vcs_settings(settings):
488 def _sanitize_vcs_settings(settings):
498 """
489 """
499 Applies settings defaults and does type conversion for all VCS related
490 Applies settings defaults and does type conversion for all VCS related
500 settings.
491 settings.
501 """
492 """
502 _string_setting(settings, 'vcs.svn.compatible_version', '')
493 _string_setting(settings, 'vcs.svn.compatible_version', '')
503 _string_setting(settings, 'git_rev_filter', '--all')
494 _string_setting(settings, 'git_rev_filter', '--all')
504 _string_setting(settings, 'vcs.hooks.protocol', 'http')
495 _string_setting(settings, 'vcs.hooks.protocol', 'http')
505 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
496 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
506 _string_setting(settings, 'vcs.server', '')
497 _string_setting(settings, 'vcs.server', '')
507 _string_setting(settings, 'vcs.server.log_level', 'debug')
498 _string_setting(settings, 'vcs.server.log_level', 'debug')
508 _string_setting(settings, 'vcs.server.protocol', 'http')
499 _string_setting(settings, 'vcs.server.protocol', 'http')
509 _bool_setting(settings, 'startup.import_repos', 'false')
500 _bool_setting(settings, 'startup.import_repos', 'false')
510 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
501 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
511 _bool_setting(settings, 'vcs.server.enable', 'true')
502 _bool_setting(settings, 'vcs.server.enable', 'true')
512 _bool_setting(settings, 'vcs.start_server', 'false')
503 _bool_setting(settings, 'vcs.start_server', 'false')
513 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
504 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
514 _int_setting(settings, 'vcs.connection_timeout', 3600)
505 _int_setting(settings, 'vcs.connection_timeout', 3600)
515
506
516 # Support legacy values of vcs.scm_app_implementation. Legacy
507 # Support legacy values of vcs.scm_app_implementation. Legacy
517 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
508 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
518 # which is now mapped to 'http'.
509 # which is now mapped to 'http'.
519 scm_app_impl = settings['vcs.scm_app_implementation']
510 scm_app_impl = settings['vcs.scm_app_implementation']
520 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
511 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
521 settings['vcs.scm_app_implementation'] = 'http'
512 settings['vcs.scm_app_implementation'] = 'http'
522
513
523
514
524 def _int_setting(settings, name, default):
515 def _int_setting(settings, name, default):
525 settings[name] = int(settings.get(name, default))
516 settings[name] = int(settings.get(name, default))
526
517
527
518
528 def _bool_setting(settings, name, default):
519 def _bool_setting(settings, name, default):
529 input = settings.get(name, default)
520 input = settings.get(name, default)
530 if isinstance(input, unicode):
521 if isinstance(input, unicode):
531 input = input.encode('utf8')
522 input = input.encode('utf8')
532 settings[name] = asbool(input)
523 settings[name] = asbool(input)
533
524
534
525
535 def _list_setting(settings, name, default):
526 def _list_setting(settings, name, default):
536 raw_value = settings.get(name, default)
527 raw_value = settings.get(name, default)
537
528
538 old_separator = ','
529 old_separator = ','
539 if old_separator in raw_value:
530 if old_separator in raw_value:
540 # If we get a comma separated list, pass it to our own function.
531 # If we get a comma separated list, pass it to our own function.
541 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
532 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
542 else:
533 else:
543 # Otherwise we assume it uses pyramids space/newline separation.
534 # Otherwise we assume it uses pyramids space/newline separation.
544 settings[name] = aslist(raw_value)
535 settings[name] = aslist(raw_value)
545
536
546
537
547 def _string_setting(settings, name, default, lower=True):
538 def _string_setting(settings, name, default, lower=True):
548 value = settings.get(name, default)
539 value = settings.get(name, default)
549 if lower:
540 if lower:
550 value = value.lower()
541 value = value.lower()
551 settings[name] = value
542 settings[name] = value
General Comments 0
You need to be logged in to leave comments. Login now