##// END OF EJS Templates
events: use a distinction between RhodeCodeEvent which is a base class and it used by all events, and...
marcink -
r2921:042146f6 default
parent child
Show More
@@ -1,522 +1,518
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 traceback
24 import traceback
25 import collections
25 import collections
26 import tempfile
26 import tempfile
27
27
28 from paste.gzipper import make_gzip_middleware
28 from paste.gzipper import make_gzip_middleware
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.events import ApplicationCreated
35 from pyramid.events import ApplicationCreated
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.environment import load_pyramid_environment
41 from rhodecode.config.environment import load_pyramid_environment
42
42
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.caches')
235 config.include('rhodecode.lib.caches')
236 config.include('rhodecode.lib.rc_cache')
236 config.include('rhodecode.lib.rc_cache')
237
237
238 config.include('rhodecode.authentication')
238 config.include('rhodecode.authentication')
239 config.include('rhodecode.integrations')
239 config.include('rhodecode.integrations')
240
240
241 # apps
241 # apps
242 config.include('rhodecode.apps._base')
242 config.include('rhodecode.apps._base')
243 config.include('rhodecode.apps.ops')
243 config.include('rhodecode.apps.ops')
244
244
245 config.include('rhodecode.apps.admin')
245 config.include('rhodecode.apps.admin')
246 config.include('rhodecode.apps.channelstream')
246 config.include('rhodecode.apps.channelstream')
247 config.include('rhodecode.apps.login')
247 config.include('rhodecode.apps.login')
248 config.include('rhodecode.apps.home')
248 config.include('rhodecode.apps.home')
249 config.include('rhodecode.apps.journal')
249 config.include('rhodecode.apps.journal')
250 config.include('rhodecode.apps.repository')
250 config.include('rhodecode.apps.repository')
251 config.include('rhodecode.apps.repo_group')
251 config.include('rhodecode.apps.repo_group')
252 config.include('rhodecode.apps.user_group')
252 config.include('rhodecode.apps.user_group')
253 config.include('rhodecode.apps.search')
253 config.include('rhodecode.apps.search')
254 config.include('rhodecode.apps.user_profile')
254 config.include('rhodecode.apps.user_profile')
255 config.include('rhodecode.apps.user_group_profile')
255 config.include('rhodecode.apps.user_group_profile')
256 config.include('rhodecode.apps.my_account')
256 config.include('rhodecode.apps.my_account')
257 config.include('rhodecode.apps.svn_support')
257 config.include('rhodecode.apps.svn_support')
258 config.include('rhodecode.apps.ssh_support')
258 config.include('rhodecode.apps.ssh_support')
259 config.include('rhodecode.apps.gist')
259 config.include('rhodecode.apps.gist')
260
260
261 config.include('rhodecode.apps.debug_style')
261 config.include('rhodecode.apps.debug_style')
262 config.include('rhodecode.tweens')
262 config.include('rhodecode.tweens')
263 config.include('rhodecode.api')
263 config.include('rhodecode.api')
264
264
265 config.add_route(
265 config.add_route(
266 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
266 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
267
267
268 config.add_translation_dirs('rhodecode:i18n/')
268 config.add_translation_dirs('rhodecode:i18n/')
269 settings['default_locale_name'] = settings.get('lang', 'en')
269 settings['default_locale_name'] = settings.get('lang', 'en')
270
270
271 # Add subscribers.
271 # Add subscribers.
272 config.add_subscriber(inject_app_settings, ApplicationCreated)
272 config.add_subscriber(inject_app_settings, ApplicationCreated)
273 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
273 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
274 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
274 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
275 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
275 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
276
276
277 # events
278 # TODO(marcink): this should be done when pyramid migration is finished
279 # config.add_subscriber(
280 # 'rhodecode.integrations.integrations_event_handler',
281 # 'rhodecode.events.RhodecodeEvent')
282
277
283 # request custom methods
278 # request custom methods
284 config.add_request_method(
279 config.add_request_method(
285 'rhodecode.lib.partial_renderer.get_partial_renderer',
280 'rhodecode.lib.partial_renderer.get_partial_renderer',
286 'get_partial_renderer')
281 'get_partial_renderer')
287
282
288 # Set the authorization policy.
283 # Set the authorization policy.
289 authz_policy = ACLAuthorizationPolicy()
284 authz_policy = ACLAuthorizationPolicy()
290 config.set_authorization_policy(authz_policy)
285 config.set_authorization_policy(authz_policy)
291
286
292 # Set the default renderer for HTML templates to mako.
287 # Set the default renderer for HTML templates to mako.
293 config.add_mako_renderer('.html')
288 config.add_mako_renderer('.html')
294
289
295 config.add_renderer(
290 config.add_renderer(
296 name='json_ext',
291 name='json_ext',
297 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
292 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
298
293
299 # include RhodeCode plugins
294 # include RhodeCode plugins
300 includes = aslist(settings.get('rhodecode.includes', []))
295 includes = aslist(settings.get('rhodecode.includes', []))
301 for inc in includes:
296 for inc in includes:
302 config.include(inc)
297 config.include(inc)
303
298
304 # custom not found view, if our pyramid app doesn't know how to handle
299 # custom not found view, if our pyramid app doesn't know how to handle
305 # the request pass it to potential VCS handling ap
300 # the request pass it to potential VCS handling ap
306 config.add_notfound_view(not_found_view)
301 config.add_notfound_view(not_found_view)
307 if not settings.get('debugtoolbar.enabled', False):
302 if not settings.get('debugtoolbar.enabled', False):
308 # disabled debugtoolbar handle all exceptions via the error_handlers
303 # disabled debugtoolbar handle all exceptions via the error_handlers
309 config.add_view(error_handler, context=Exception)
304 config.add_view(error_handler, context=Exception)
310
305
311 # all errors including 403/404/50X
306 # all errors including 403/404/50X
312 config.add_view(error_handler, context=HTTPError)
307 config.add_view(error_handler, context=HTTPError)
313
308
314
309
315 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
310 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
316 """
311 """
317 Apply outer WSGI middlewares around the application.
312 Apply outer WSGI middlewares around the application.
318 """
313 """
319 settings = config.registry.settings
314 registry = config.registry
315 settings = registry.settings
320
316
321 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
317 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
322 pyramid_app = HttpsFixup(pyramid_app, settings)
318 pyramid_app = HttpsFixup(pyramid_app, settings)
323
319
324 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
320 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
325 pyramid_app, settings)
321 pyramid_app, settings)
326 config.registry.ae_client = _ae_client
322 registry.ae_client = _ae_client
327
323
328 if settings['gzip_responses']:
324 if settings['gzip_responses']:
329 pyramid_app = make_gzip_middleware(
325 pyramid_app = make_gzip_middleware(
330 pyramid_app, settings, compress_level=1)
326 pyramid_app, settings, compress_level=1)
331
327
332 # this should be the outer most middleware in the wsgi stack since
328 # this should be the outer most middleware in the wsgi stack since
333 # middleware like Routes make database calls
329 # middleware like Routes make database calls
334 def pyramid_app_with_cleanup(environ, start_response):
330 def pyramid_app_with_cleanup(environ, start_response):
335 try:
331 try:
336 return pyramid_app(environ, start_response)
332 return pyramid_app(environ, start_response)
337 finally:
333 finally:
338 # Dispose current database session and rollback uncommitted
334 # Dispose current database session and rollback uncommitted
339 # transactions.
335 # transactions.
340 meta.Session.remove()
336 meta.Session.remove()
341
337
342 # In a single threaded mode server, on non sqlite db we should have
338 # In a single threaded mode server, on non sqlite db we should have
343 # '0 Current Checked out connections' at the end of a request,
339 # '0 Current Checked out connections' at the end of a request,
344 # if not, then something, somewhere is leaving a connection open
340 # if not, then something, somewhere is leaving a connection open
345 pool = meta.Base.metadata.bind.engine.pool
341 pool = meta.Base.metadata.bind.engine.pool
346 log.debug('sa pool status: %s', pool.status())
342 log.debug('sa pool status: %s', pool.status())
347
343
348 return pyramid_app_with_cleanup
344 return pyramid_app_with_cleanup
349
345
350
346
351 def sanitize_settings_and_apply_defaults(settings):
347 def sanitize_settings_and_apply_defaults(settings):
352 """
348 """
353 Applies settings defaults and does all type conversion.
349 Applies settings defaults and does all type conversion.
354
350
355 We would move all settings parsing and preparation into this place, so that
351 We would move all settings parsing and preparation into this place, so that
356 we have only one place left which deals with this part. The remaining parts
352 we have only one place left which deals with this part. The remaining parts
357 of the application would start to rely fully on well prepared settings.
353 of the application would start to rely fully on well prepared settings.
358
354
359 This piece would later be split up per topic to avoid a big fat monster
355 This piece would later be split up per topic to avoid a big fat monster
360 function.
356 function.
361 """
357 """
362
358
363 settings.setdefault('rhodecode.edition', 'Community Edition')
359 settings.setdefault('rhodecode.edition', 'Community Edition')
364
360
365 if 'mako.default_filters' not in settings:
361 if 'mako.default_filters' not in settings:
366 # set custom default filters if we don't have it defined
362 # set custom default filters if we don't have it defined
367 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
363 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
368 settings['mako.default_filters'] = 'h_filter'
364 settings['mako.default_filters'] = 'h_filter'
369
365
370 if 'mako.directories' not in settings:
366 if 'mako.directories' not in settings:
371 mako_directories = settings.setdefault('mako.directories', [
367 mako_directories = settings.setdefault('mako.directories', [
372 # Base templates of the original application
368 # Base templates of the original application
373 'rhodecode:templates',
369 'rhodecode:templates',
374 ])
370 ])
375 log.debug(
371 log.debug(
376 "Using the following Mako template directories: %s",
372 "Using the following Mako template directories: %s",
377 mako_directories)
373 mako_directories)
378
374
379 # Default includes, possible to change as a user
375 # Default includes, possible to change as a user
380 pyramid_includes = settings.setdefault('pyramid.includes', [
376 pyramid_includes = settings.setdefault('pyramid.includes', [
381 'rhodecode.lib.middleware.request_wrapper',
377 'rhodecode.lib.middleware.request_wrapper',
382 ])
378 ])
383 log.debug(
379 log.debug(
384 "Using the following pyramid.includes: %s",
380 "Using the following pyramid.includes: %s",
385 pyramid_includes)
381 pyramid_includes)
386
382
387 # TODO: johbo: Re-think this, usually the call to config.include
383 # TODO: johbo: Re-think this, usually the call to config.include
388 # should allow to pass in a prefix.
384 # should allow to pass in a prefix.
389 settings.setdefault('rhodecode.api.url', '/_admin/api')
385 settings.setdefault('rhodecode.api.url', '/_admin/api')
390
386
391 # Sanitize generic settings.
387 # Sanitize generic settings.
392 _list_setting(settings, 'default_encoding', 'UTF-8')
388 _list_setting(settings, 'default_encoding', 'UTF-8')
393 _bool_setting(settings, 'is_test', 'false')
389 _bool_setting(settings, 'is_test', 'false')
394 _bool_setting(settings, 'gzip_responses', 'false')
390 _bool_setting(settings, 'gzip_responses', 'false')
395
391
396 # Call split out functions that sanitize settings for each topic.
392 # Call split out functions that sanitize settings for each topic.
397 _sanitize_appenlight_settings(settings)
393 _sanitize_appenlight_settings(settings)
398 _sanitize_vcs_settings(settings)
394 _sanitize_vcs_settings(settings)
399 _sanitize_cache_settings(settings)
395 _sanitize_cache_settings(settings)
400
396
401 # configure instance id
397 # configure instance id
402 config_utils.set_instance_id(settings)
398 config_utils.set_instance_id(settings)
403
399
404 return settings
400 return settings
405
401
406
402
407 def _sanitize_appenlight_settings(settings):
403 def _sanitize_appenlight_settings(settings):
408 _bool_setting(settings, 'appenlight', 'false')
404 _bool_setting(settings, 'appenlight', 'false')
409
405
410
406
411 def _sanitize_vcs_settings(settings):
407 def _sanitize_vcs_settings(settings):
412 """
408 """
413 Applies settings defaults and does type conversion for all VCS related
409 Applies settings defaults and does type conversion for all VCS related
414 settings.
410 settings.
415 """
411 """
416 _string_setting(settings, 'vcs.svn.compatible_version', '')
412 _string_setting(settings, 'vcs.svn.compatible_version', '')
417 _string_setting(settings, 'git_rev_filter', '--all')
413 _string_setting(settings, 'git_rev_filter', '--all')
418 _string_setting(settings, 'vcs.hooks.protocol', 'http')
414 _string_setting(settings, 'vcs.hooks.protocol', 'http')
419 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
415 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
420 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
416 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
421 _string_setting(settings, 'vcs.server', '')
417 _string_setting(settings, 'vcs.server', '')
422 _string_setting(settings, 'vcs.server.log_level', 'debug')
418 _string_setting(settings, 'vcs.server.log_level', 'debug')
423 _string_setting(settings, 'vcs.server.protocol', 'http')
419 _string_setting(settings, 'vcs.server.protocol', 'http')
424 _bool_setting(settings, 'startup.import_repos', 'false')
420 _bool_setting(settings, 'startup.import_repos', 'false')
425 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
421 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
426 _bool_setting(settings, 'vcs.server.enable', 'true')
422 _bool_setting(settings, 'vcs.server.enable', 'true')
427 _bool_setting(settings, 'vcs.start_server', 'false')
423 _bool_setting(settings, 'vcs.start_server', 'false')
428 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
424 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
429 _int_setting(settings, 'vcs.connection_timeout', 3600)
425 _int_setting(settings, 'vcs.connection_timeout', 3600)
430
426
431 # Support legacy values of vcs.scm_app_implementation. Legacy
427 # Support legacy values of vcs.scm_app_implementation. Legacy
432 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
428 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
433 # which is now mapped to 'http'.
429 # which is now mapped to 'http'.
434 scm_app_impl = settings['vcs.scm_app_implementation']
430 scm_app_impl = settings['vcs.scm_app_implementation']
435 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
431 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
436 settings['vcs.scm_app_implementation'] = 'http'
432 settings['vcs.scm_app_implementation'] = 'http'
437
433
438
434
439 def _sanitize_cache_settings(settings):
435 def _sanitize_cache_settings(settings):
440 _string_setting(settings, 'cache_dir',
436 _string_setting(settings, 'cache_dir',
441 os.path.join(tempfile.gettempdir(), 'rc_cache'))
437 os.path.join(tempfile.gettempdir(), 'rc_cache'))
442 # cache_perms
438 # cache_perms
443 _string_setting(
439 _string_setting(
444 settings,
440 settings,
445 'rc_cache.cache_perms.backend',
441 'rc_cache.cache_perms.backend',
446 'dogpile.cache.rc.file_namespace')
442 'dogpile.cache.rc.file_namespace')
447 _int_setting(
443 _int_setting(
448 settings,
444 settings,
449 'rc_cache.cache_perms.expiration_time',
445 'rc_cache.cache_perms.expiration_time',
450 60)
446 60)
451 _string_setting(
447 _string_setting(
452 settings,
448 settings,
453 'rc_cache.cache_perms.arguments.filename',
449 'rc_cache.cache_perms.arguments.filename',
454 os.path.join(tempfile.gettempdir(), 'rc_cache_1'))
450 os.path.join(tempfile.gettempdir(), 'rc_cache_1'))
455
451
456 # cache_repo
452 # cache_repo
457 _string_setting(
453 _string_setting(
458 settings,
454 settings,
459 'rc_cache.cache_repo.backend',
455 'rc_cache.cache_repo.backend',
460 'dogpile.cache.rc.file_namespace')
456 'dogpile.cache.rc.file_namespace')
461 _int_setting(
457 _int_setting(
462 settings,
458 settings,
463 'rc_cache.cache_repo.expiration_time',
459 'rc_cache.cache_repo.expiration_time',
464 60)
460 60)
465 _string_setting(
461 _string_setting(
466 settings,
462 settings,
467 'rc_cache.cache_repo.arguments.filename',
463 'rc_cache.cache_repo.arguments.filename',
468 os.path.join(tempfile.gettempdir(), 'rc_cache_2'))
464 os.path.join(tempfile.gettempdir(), 'rc_cache_2'))
469
465
470 # sql_cache_short
466 # sql_cache_short
471 _string_setting(
467 _string_setting(
472 settings,
468 settings,
473 'rc_cache.sql_cache_short.backend',
469 'rc_cache.sql_cache_short.backend',
474 'dogpile.cache.rc.memory_lru')
470 'dogpile.cache.rc.memory_lru')
475 _int_setting(
471 _int_setting(
476 settings,
472 settings,
477 'rc_cache.sql_cache_short.expiration_time',
473 'rc_cache.sql_cache_short.expiration_time',
478 30)
474 30)
479 _int_setting(
475 _int_setting(
480 settings,
476 settings,
481 'rc_cache.sql_cache_short.max_size',
477 'rc_cache.sql_cache_short.max_size',
482 10000)
478 10000)
483
479
484
480
485 def _int_setting(settings, name, default):
481 def _int_setting(settings, name, default):
486 settings[name] = int(settings.get(name, default))
482 settings[name] = int(settings.get(name, default))
487
483
488
484
489 def _bool_setting(settings, name, default):
485 def _bool_setting(settings, name, default):
490 input_val = settings.get(name, default)
486 input_val = settings.get(name, default)
491 if isinstance(input_val, unicode):
487 if isinstance(input_val, unicode):
492 input_val = input_val.encode('utf8')
488 input_val = input_val.encode('utf8')
493 settings[name] = asbool(input_val)
489 settings[name] = asbool(input_val)
494
490
495
491
496 def _list_setting(settings, name, default):
492 def _list_setting(settings, name, default):
497 raw_value = settings.get(name, default)
493 raw_value = settings.get(name, default)
498
494
499 old_separator = ','
495 old_separator = ','
500 if old_separator in raw_value:
496 if old_separator in raw_value:
501 # If we get a comma separated list, pass it to our own function.
497 # If we get a comma separated list, pass it to our own function.
502 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
498 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
503 else:
499 else:
504 # Otherwise we assume it uses pyramids space/newline separation.
500 # Otherwise we assume it uses pyramids space/newline separation.
505 settings[name] = aslist(raw_value)
501 settings[name] = aslist(raw_value)
506
502
507
503
508 def _string_setting(settings, name, default, lower=True):
504 def _string_setting(settings, name, default, lower=True):
509 value = settings.get(name, default)
505 value = settings.get(name, default)
510 if lower:
506 if lower:
511 value = value.lower()
507 value = value.lower()
512 settings[name] = value
508 settings[name] = value
513
509
514
510
515 def _substitute_values(mapping, substitutions):
511 def _substitute_values(mapping, substitutions):
516 result = {
512 result = {
517 # Note: Cannot use regular replacements, since they would clash
513 # Note: Cannot use regular replacements, since they would clash
518 # with the implementation of ConfigParser. Using "format" instead.
514 # with the implementation of ConfigParser. Using "format" instead.
519 key: value.format(**substitutions)
515 key: value.format(**substitutions)
520 for key, value in mapping.items()
516 for key, value in mapping.items()
521 }
517 }
522 return result
518 return result
@@ -1,84 +1,76
1 # Copyright (C) 2016-2018 RhodeCode GmbH
1 # Copyright (C) 2016-2018 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU Affero General Public License
12 # You should have received a copy of the GNU Affero General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 #
14 #
15 # This program is dual-licensed. If you wish to learn more about the
15 # This program is dual-licensed. If you wish to learn more about the
16 # RhodeCode Enterprise Edition, including its added features, Support services,
16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19 import logging
19 import logging
20 from pyramid.threadlocal import get_current_registry
20 from pyramid.threadlocal import get_current_registry
21 from rhodecode.events.base import RhodecodeEvent
21 from rhodecode.events.base import RhodeCodeIntegrationEvent
22
22
23
23
24 log = logging.getLogger(__name__)
24 log = logging.getLogger(__name__)
25
25
26
26
27 def trigger(event, registry=None):
27 def trigger(event, registry=None):
28 """
28 """
29 Helper method to send an event. This wraps the pyramid logic to send an
29 Helper method to send an event. This wraps the pyramid logic to send an
30 event.
30 event.
31 """
31 """
32 # For the first step we are using pyramids thread locals here. If the
32 # For the first step we are using pyramids thread locals here. If the
33 # event mechanism works out as a good solution we should think about
33 # event mechanism works out as a good solution we should think about
34 # passing the registry as an argument to get rid of it.
34 # passing the registry as an argument to get rid of it.
35 registry = registry or get_current_registry()
35 registry = registry or get_current_registry()
36 registry.notify(event)
36 registry.notify(event)
37 log.debug('event %s triggered using registry %s', event.__class__, registry)
37 log.debug('event %s triggered using registry %s', event.__class__, registry)
38
38
39 # Until we can work around the problem that VCS operations do not have a
39 # Send the events to integrations directly
40 # pyramid context to work with, we send the events to integrations directly
41
42 # Later it will be possible to use regular pyramid subscribers ie:
43 # config.add_subscriber(
44 # 'rhodecode.integrations.integrations_event_handler',
45 # 'rhodecode.events.RhodecodeEvent')
46 # trigger(event, request.registry)
47
48 from rhodecode.integrations import integrations_event_handler
40 from rhodecode.integrations import integrations_event_handler
49 if isinstance(event, RhodecodeEvent):
41 if isinstance(event, RhodeCodeIntegrationEvent):
50 integrations_event_handler(event)
42 integrations_event_handler(event)
51
43
52
44
53 from rhodecode.events.user import ( # noqa
45 from rhodecode.events.user import ( # noqa
54 UserPreCreate,
46 UserPreCreate,
55 UserPostCreate,
47 UserPostCreate,
56 UserPreUpdate,
48 UserPreUpdate,
57 UserRegistered,
49 UserRegistered,
58 UserPermissionsChange,
50 UserPermissionsChange,
59 )
51 )
60
52
61 from rhodecode.events.repo import ( # noqa
53 from rhodecode.events.repo import ( # noqa
62 RepoEvent,
54 RepoEvent,
63 RepoPreCreateEvent, RepoCreateEvent,
55 RepoPreCreateEvent, RepoCreateEvent,
64 RepoPreDeleteEvent, RepoDeleteEvent,
56 RepoPreDeleteEvent, RepoDeleteEvent,
65 RepoPrePushEvent, RepoPushEvent,
57 RepoPrePushEvent, RepoPushEvent,
66 RepoPrePullEvent, RepoPullEvent,
58 RepoPrePullEvent, RepoPullEvent,
67 )
59 )
68
60
69 from rhodecode.events.repo_group import ( # noqa
61 from rhodecode.events.repo_group import ( # noqa
70 RepoGroupEvent,
62 RepoGroupEvent,
71 RepoGroupCreateEvent,
63 RepoGroupCreateEvent,
72 RepoGroupUpdateEvent,
64 RepoGroupUpdateEvent,
73 RepoGroupDeleteEvent,
65 RepoGroupDeleteEvent,
74 )
66 )
75
67
76 from rhodecode.events.pullrequest import ( # noqa
68 from rhodecode.events.pullrequest import ( # noqa
77 PullRequestEvent,
69 PullRequestEvent,
78 PullRequestCreateEvent,
70 PullRequestCreateEvent,
79 PullRequestUpdateEvent,
71 PullRequestUpdateEvent,
80 PullRequestCommentEvent,
72 PullRequestCommentEvent,
81 PullRequestReviewEvent,
73 PullRequestReviewEvent,
82 PullRequestMergeEvent,
74 PullRequestMergeEvent,
83 PullRequestCloseEvent,
75 PullRequestCloseEvent,
84 )
76 )
@@ -1,112 +1,118
1 # Copyright (C) 2016-2018 RhodeCode GmbH
1 # Copyright (C) 2016-2018 RhodeCode GmbH
2 #
2 #
3 # This program is free software: you can redistribute it and/or modify
3 # This program is free software: you can redistribute it and/or modify
4 # it under the terms of the GNU Affero General Public License, version 3
4 # it under the terms of the GNU Affero General Public License, version 3
5 # (only), as published by the Free Software Foundation.
5 # (only), as published by the Free Software Foundation.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,