##// END OF EJS Templates
redis: fixed new redis url schema requirement for connection string
marcink -
r3867:2bc3e4d7 default
parent child Browse files
Show More
@@ -1,740 +1,746 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import sys
22 import sys
23 import logging
23 import logging
24 import collections
24 import collections
25 import tempfile
25 import tempfile
26 import time
26 import time
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.environment import load_pyramid_environment
41 from rhodecode.config.environment import load_pyramid_environment
42
42
43 import rhodecode.events
43 import rhodecode.events
44 from rhodecode.lib.middleware.vcs import VCSMiddleware
44 from rhodecode.lib.middleware.vcs import VCSMiddleware
45 from rhodecode.lib.request import Request
45 from rhodecode.lib.request import Request
46 from rhodecode.lib.vcs import VCSCommunicationError
46 from rhodecode.lib.vcs import VCSCommunicationError
47 from rhodecode.lib.exceptions import VCSServerUnavailable
47 from rhodecode.lib.exceptions import VCSServerUnavailable
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
50 from rhodecode.lib.celerylib.loader import configure_celery
50 from rhodecode.lib.celerylib.loader import configure_celery
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 aslist as rhodecode_aslist, AttributeDict
52 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, 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, inject_app_settings)
56 write_metadata_if_needed, inject_app_settings)
57
57
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 return True
72 return True
73
73
74
74
75 def make_pyramid_app(global_config, **settings):
75 def make_pyramid_app(global_config, **settings):
76 """
76 """
77 Constructs the WSGI application based on Pyramid.
77 Constructs the WSGI application based on Pyramid.
78
78
79 Specials:
79 Specials:
80
80
81 * The application can also be integrated like a plugin via the call to
81 * The application can also be integrated like a plugin via the call to
82 `includeme`. This is accompanied with the other utility functions which
82 `includeme`. This is accompanied with the other utility functions which
83 are called. Changing this should be done with great care to not break
83 are called. Changing this should be done with great care to not break
84 cases when these fragments are assembled from another place.
84 cases when these fragments are assembled from another place.
85
85
86 """
86 """
87
87
88 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
88 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
89 # will be replaced by the value of the environment variable "NAME" in this case.
89 # will be replaced by the value of the environment variable "NAME" in this case.
90 start_time = time.time()
90 start_time = time.time()
91
91
92 debug = asbool(global_config.get('debug'))
92 debug = asbool(global_config.get('debug'))
93 if debug:
93 if debug:
94 enable_debug()
94 enable_debug()
95
95
96 environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()}
96 environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()}
97
97
98 global_config = _substitute_values(global_config, environ)
98 global_config = _substitute_values(global_config, environ)
99 settings = _substitute_values(settings, environ)
99 settings = _substitute_values(settings, environ)
100
100
101 sanitize_settings_and_apply_defaults(global_config, settings)
101 sanitize_settings_and_apply_defaults(global_config, settings)
102
102
103 config = Configurator(settings=settings)
103 config = Configurator(settings=settings)
104
104
105 # Apply compatibility patches
105 # Apply compatibility patches
106 patches.inspect_getargspec()
106 patches.inspect_getargspec()
107
107
108 load_pyramid_environment(global_config, settings)
108 load_pyramid_environment(global_config, settings)
109
109
110 # Static file view comes first
110 # Static file view comes first
111 includeme_first(config)
111 includeme_first(config)
112
112
113 includeme(config)
113 includeme(config)
114
114
115 pyramid_app = config.make_wsgi_app()
115 pyramid_app = config.make_wsgi_app()
116 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
116 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
117 pyramid_app.config = config
117 pyramid_app.config = config
118
118
119 config.configure_celery(global_config['__file__'])
119 config.configure_celery(global_config['__file__'])
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 total_time = time.time() - start_time
122 total_time = time.time() - start_time
123 log.info('Pyramid app `%s` created and configured in %.2fs',
123 log.info('Pyramid app `%s` created and configured in %.2fs',
124 pyramid_app.func_name, total_time)
124 pyramid_app.func_name, total_time)
125
125
126 return pyramid_app
126 return pyramid_app
127
127
128
128
129 def not_found_view(request):
129 def not_found_view(request):
130 """
130 """
131 This creates the view which should be registered as not-found-view to
131 This creates the view which should be registered as not-found-view to
132 pyramid.
132 pyramid.
133 """
133 """
134
134
135 if not getattr(request, 'vcs_call', None):
135 if not getattr(request, 'vcs_call', None):
136 # handle like regular case with our error_handler
136 # handle like regular case with our error_handler
137 return error_handler(HTTPNotFound(), request)
137 return error_handler(HTTPNotFound(), request)
138
138
139 # handle not found view as a vcs call
139 # handle not found view as a vcs call
140 settings = request.registry.settings
140 settings = request.registry.settings
141 ae_client = getattr(request, 'ae_client', None)
141 ae_client = getattr(request, 'ae_client', None)
142 vcs_app = VCSMiddleware(
142 vcs_app = VCSMiddleware(
143 HTTPNotFound(), request.registry, settings,
143 HTTPNotFound(), request.registry, settings,
144 appenlight_client=ae_client)
144 appenlight_client=ae_client)
145
145
146 return wsgiapp(vcs_app)(None, request)
146 return wsgiapp(vcs_app)(None, request)
147
147
148
148
149 def error_handler(exception, request):
149 def error_handler(exception, request):
150 import rhodecode
150 import rhodecode
151 from rhodecode.lib import helpers
151 from rhodecode.lib import helpers
152
152
153 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
153 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
154
154
155 base_response = HTTPInternalServerError()
155 base_response = HTTPInternalServerError()
156 # prefer original exception for the response since it may have headers set
156 # prefer original exception for the response since it may have headers set
157 if isinstance(exception, HTTPException):
157 if isinstance(exception, HTTPException):
158 base_response = exception
158 base_response = exception
159 elif isinstance(exception, VCSCommunicationError):
159 elif isinstance(exception, VCSCommunicationError):
160 base_response = VCSServerUnavailable()
160 base_response = VCSServerUnavailable()
161
161
162 if is_http_error(base_response):
162 if is_http_error(base_response):
163 log.exception(
163 log.exception(
164 'error occurred handling this request for path: %s', request.path)
164 'error occurred handling this request for path: %s', request.path)
165
165
166 error_explanation = base_response.explanation or str(base_response)
166 error_explanation = base_response.explanation or str(base_response)
167 if base_response.status_code == 404:
167 if base_response.status_code == 404:
168 error_explanation += " Optionally you don't have permission to access this page."
168 error_explanation += " Optionally you don't have permission to access this page."
169 c = AttributeDict()
169 c = AttributeDict()
170 c.error_message = base_response.status
170 c.error_message = base_response.status
171 c.error_explanation = error_explanation
171 c.error_explanation = error_explanation
172 c.visual = AttributeDict()
172 c.visual = AttributeDict()
173
173
174 c.visual.rhodecode_support_url = (
174 c.visual.rhodecode_support_url = (
175 request.registry.settings.get('rhodecode_support_url') or
175 request.registry.settings.get('rhodecode_support_url') or
176 request.route_url('rhodecode_support')
176 request.route_url('rhodecode_support')
177 )
177 )
178 c.redirect_time = 0
178 c.redirect_time = 0
179 c.rhodecode_name = rhodecode_title
179 c.rhodecode_name = rhodecode_title
180 if not c.rhodecode_name:
180 if not c.rhodecode_name:
181 c.rhodecode_name = 'Rhodecode'
181 c.rhodecode_name = 'Rhodecode'
182
182
183 c.causes = []
183 c.causes = []
184 if is_http_error(base_response):
184 if is_http_error(base_response):
185 c.causes.append('Server is overloaded.')
185 c.causes.append('Server is overloaded.')
186 c.causes.append('Server database connection is lost.')
186 c.causes.append('Server database connection is lost.')
187 c.causes.append('Server expected unhandled error.')
187 c.causes.append('Server expected unhandled error.')
188
188
189 if hasattr(base_response, 'causes'):
189 if hasattr(base_response, 'causes'):
190 c.causes = base_response.causes
190 c.causes = base_response.causes
191
191
192 c.messages = helpers.flash.pop_messages(request=request)
192 c.messages = helpers.flash.pop_messages(request=request)
193
193
194 exc_info = sys.exc_info()
194 exc_info = sys.exc_info()
195 c.exception_id = id(exc_info)
195 c.exception_id = id(exc_info)
196 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
196 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
197 or base_response.status_code > 499
197 or base_response.status_code > 499
198 c.exception_id_url = request.route_url(
198 c.exception_id_url = request.route_url(
199 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
199 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
200
200
201 if c.show_exception_id:
201 if c.show_exception_id:
202 store_exception(c.exception_id, exc_info)
202 store_exception(c.exception_id, exc_info)
203
203
204 response = render_to_response(
204 response = render_to_response(
205 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
205 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
206 response=base_response)
206 response=base_response)
207
207
208 return response
208 return response
209
209
210
210
211 def includeme_first(config):
211 def includeme_first(config):
212 # redirect automatic browser favicon.ico requests to correct place
212 # redirect automatic browser favicon.ico requests to correct place
213 def favicon_redirect(context, request):
213 def favicon_redirect(context, request):
214 return HTTPFound(
214 return HTTPFound(
215 request.static_path('rhodecode:public/images/favicon.ico'))
215 request.static_path('rhodecode:public/images/favicon.ico'))
216
216
217 config.add_view(favicon_redirect, route_name='favicon')
217 config.add_view(favicon_redirect, route_name='favicon')
218 config.add_route('favicon', '/favicon.ico')
218 config.add_route('favicon', '/favicon.ico')
219
219
220 def robots_redirect(context, request):
220 def robots_redirect(context, request):
221 return HTTPFound(
221 return HTTPFound(
222 request.static_path('rhodecode:public/robots.txt'))
222 request.static_path('rhodecode:public/robots.txt'))
223
223
224 config.add_view(robots_redirect, route_name='robots')
224 config.add_view(robots_redirect, route_name='robots')
225 config.add_route('robots', '/robots.txt')
225 config.add_route('robots', '/robots.txt')
226
226
227 config.add_static_view(
227 config.add_static_view(
228 '_static/deform', 'deform:static')
228 '_static/deform', 'deform:static')
229 config.add_static_view(
229 config.add_static_view(
230 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
230 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
231
231
232
232
233 def includeme(config):
233 def includeme(config):
234 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
234 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
235 settings = config.registry.settings
235 settings = config.registry.settings
236 config.set_request_factory(Request)
236 config.set_request_factory(Request)
237
237
238 # plugin information
238 # plugin information
239 config.registry.rhodecode_plugins = collections.OrderedDict()
239 config.registry.rhodecode_plugins = collections.OrderedDict()
240
240
241 config.add_directive(
241 config.add_directive(
242 'register_rhodecode_plugin', register_rhodecode_plugin)
242 'register_rhodecode_plugin', register_rhodecode_plugin)
243
243
244 config.add_directive('configure_celery', configure_celery)
244 config.add_directive('configure_celery', configure_celery)
245
245
246 if asbool(settings.get('appenlight', 'false')):
246 if asbool(settings.get('appenlight', 'false')):
247 config.include('appenlight_client.ext.pyramid_tween')
247 config.include('appenlight_client.ext.pyramid_tween')
248
248
249 load_all = should_load_all()
249 load_all = should_load_all()
250
250
251 # Includes which are required. The application would fail without them.
251 # Includes which are required. The application would fail without them.
252 config.include('pyramid_mako')
252 config.include('pyramid_mako')
253 config.include('rhodecode.lib.rc_beaker')
253 config.include('rhodecode.lib.rc_beaker')
254 config.include('rhodecode.lib.rc_cache')
254 config.include('rhodecode.lib.rc_cache')
255
255
256 config.include('rhodecode.apps._base.navigation')
256 config.include('rhodecode.apps._base.navigation')
257 config.include('rhodecode.apps._base.subscribers')
257 config.include('rhodecode.apps._base.subscribers')
258 config.include('rhodecode.tweens')
258 config.include('rhodecode.tweens')
259
259
260 config.include('rhodecode.integrations')
260 config.include('rhodecode.integrations')
261 config.include('rhodecode.authentication')
261 config.include('rhodecode.authentication')
262
262
263 if load_all:
263 if load_all:
264 from rhodecode.authentication import discover_legacy_plugins
264 from rhodecode.authentication import discover_legacy_plugins
265 # load CE authentication plugins
265 # load CE authentication plugins
266 config.include('rhodecode.authentication.plugins.auth_crowd')
266 config.include('rhodecode.authentication.plugins.auth_crowd')
267 config.include('rhodecode.authentication.plugins.auth_headers')
267 config.include('rhodecode.authentication.plugins.auth_headers')
268 config.include('rhodecode.authentication.plugins.auth_jasig_cas')
268 config.include('rhodecode.authentication.plugins.auth_jasig_cas')
269 config.include('rhodecode.authentication.plugins.auth_ldap')
269 config.include('rhodecode.authentication.plugins.auth_ldap')
270 config.include('rhodecode.authentication.plugins.auth_pam')
270 config.include('rhodecode.authentication.plugins.auth_pam')
271 config.include('rhodecode.authentication.plugins.auth_rhodecode')
271 config.include('rhodecode.authentication.plugins.auth_rhodecode')
272 config.include('rhodecode.authentication.plugins.auth_token')
272 config.include('rhodecode.authentication.plugins.auth_token')
273
273
274 # Auto discover authentication plugins and include their configuration.
274 # Auto discover authentication plugins and include their configuration.
275 discover_legacy_plugins(config)
275 discover_legacy_plugins(config)
276
276
277 # apps
277 # apps
278 config.include('rhodecode.apps._base')
278 config.include('rhodecode.apps._base')
279
279
280 if load_all:
280 if load_all:
281 config.include('rhodecode.apps.ops')
281 config.include('rhodecode.apps.ops')
282 config.include('rhodecode.apps.admin')
282 config.include('rhodecode.apps.admin')
283 config.include('rhodecode.apps.channelstream')
283 config.include('rhodecode.apps.channelstream')
284 config.include('rhodecode.apps.file_store')
284 config.include('rhodecode.apps.file_store')
285 config.include('rhodecode.apps.login')
285 config.include('rhodecode.apps.login')
286 config.include('rhodecode.apps.home')
286 config.include('rhodecode.apps.home')
287 config.include('rhodecode.apps.journal')
287 config.include('rhodecode.apps.journal')
288 config.include('rhodecode.apps.repository')
288 config.include('rhodecode.apps.repository')
289 config.include('rhodecode.apps.repo_group')
289 config.include('rhodecode.apps.repo_group')
290 config.include('rhodecode.apps.user_group')
290 config.include('rhodecode.apps.user_group')
291 config.include('rhodecode.apps.search')
291 config.include('rhodecode.apps.search')
292 config.include('rhodecode.apps.user_profile')
292 config.include('rhodecode.apps.user_profile')
293 config.include('rhodecode.apps.user_group_profile')
293 config.include('rhodecode.apps.user_group_profile')
294 config.include('rhodecode.apps.my_account')
294 config.include('rhodecode.apps.my_account')
295 config.include('rhodecode.apps.svn_support')
295 config.include('rhodecode.apps.svn_support')
296 config.include('rhodecode.apps.ssh_support')
296 config.include('rhodecode.apps.ssh_support')
297 config.include('rhodecode.apps.gist')
297 config.include('rhodecode.apps.gist')
298 config.include('rhodecode.apps.debug_style')
298 config.include('rhodecode.apps.debug_style')
299 config.include('rhodecode.api')
299 config.include('rhodecode.api')
300
300
301 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
301 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
302 config.add_translation_dirs('rhodecode:i18n/')
302 config.add_translation_dirs('rhodecode:i18n/')
303 settings['default_locale_name'] = settings.get('lang', 'en')
303 settings['default_locale_name'] = settings.get('lang', 'en')
304
304
305 # Add subscribers.
305 # Add subscribers.
306 config.add_subscriber(inject_app_settings,
306 config.add_subscriber(inject_app_settings,
307 pyramid.events.ApplicationCreated)
307 pyramid.events.ApplicationCreated)
308 config.add_subscriber(scan_repositories_if_enabled,
308 config.add_subscriber(scan_repositories_if_enabled,
309 pyramid.events.ApplicationCreated)
309 pyramid.events.ApplicationCreated)
310 config.add_subscriber(write_metadata_if_needed,
310 config.add_subscriber(write_metadata_if_needed,
311 pyramid.events.ApplicationCreated)
311 pyramid.events.ApplicationCreated)
312 config.add_subscriber(write_js_routes_if_enabled,
312 config.add_subscriber(write_js_routes_if_enabled,
313 pyramid.events.ApplicationCreated)
313 pyramid.events.ApplicationCreated)
314
314
315 # request custom methods
315 # request custom methods
316 config.add_request_method(
316 config.add_request_method(
317 'rhodecode.lib.partial_renderer.get_partial_renderer',
317 'rhodecode.lib.partial_renderer.get_partial_renderer',
318 'get_partial_renderer')
318 'get_partial_renderer')
319
319
320 # Set the authorization policy.
320 # Set the authorization policy.
321 authz_policy = ACLAuthorizationPolicy()
321 authz_policy = ACLAuthorizationPolicy()
322 config.set_authorization_policy(authz_policy)
322 config.set_authorization_policy(authz_policy)
323
323
324 # Set the default renderer for HTML templates to mako.
324 # Set the default renderer for HTML templates to mako.
325 config.add_mako_renderer('.html')
325 config.add_mako_renderer('.html')
326
326
327 config.add_renderer(
327 config.add_renderer(
328 name='json_ext',
328 name='json_ext',
329 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
329 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
330
330
331 # include RhodeCode plugins
331 # include RhodeCode plugins
332 includes = aslist(settings.get('rhodecode.includes', []))
332 includes = aslist(settings.get('rhodecode.includes', []))
333 for inc in includes:
333 for inc in includes:
334 config.include(inc)
334 config.include(inc)
335
335
336 # custom not found view, if our pyramid app doesn't know how to handle
336 # custom not found view, if our pyramid app doesn't know how to handle
337 # the request pass it to potential VCS handling ap
337 # the request pass it to potential VCS handling ap
338 config.add_notfound_view(not_found_view)
338 config.add_notfound_view(not_found_view)
339 if not settings.get('debugtoolbar.enabled', False):
339 if not settings.get('debugtoolbar.enabled', False):
340 # disabled debugtoolbar handle all exceptions via the error_handlers
340 # disabled debugtoolbar handle all exceptions via the error_handlers
341 config.add_view(error_handler, context=Exception)
341 config.add_view(error_handler, context=Exception)
342
342
343 # all errors including 403/404/50X
343 # all errors including 403/404/50X
344 config.add_view(error_handler, context=HTTPError)
344 config.add_view(error_handler, context=HTTPError)
345
345
346
346
347 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
347 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
348 """
348 """
349 Apply outer WSGI middlewares around the application.
349 Apply outer WSGI middlewares around the application.
350 """
350 """
351 registry = config.registry
351 registry = config.registry
352 settings = registry.settings
352 settings = registry.settings
353
353
354 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
354 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
355 pyramid_app = HttpsFixup(pyramid_app, settings)
355 pyramid_app = HttpsFixup(pyramid_app, settings)
356
356
357 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
357 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
358 pyramid_app, settings)
358 pyramid_app, settings)
359 registry.ae_client = _ae_client
359 registry.ae_client = _ae_client
360
360
361 if settings['gzip_responses']:
361 if settings['gzip_responses']:
362 pyramid_app = make_gzip_middleware(
362 pyramid_app = make_gzip_middleware(
363 pyramid_app, settings, compress_level=1)
363 pyramid_app, settings, compress_level=1)
364
364
365 # this should be the outer most middleware in the wsgi stack since
365 # this should be the outer most middleware in the wsgi stack since
366 # middleware like Routes make database calls
366 # middleware like Routes make database calls
367 def pyramid_app_with_cleanup(environ, start_response):
367 def pyramid_app_with_cleanup(environ, start_response):
368 try:
368 try:
369 return pyramid_app(environ, start_response)
369 return pyramid_app(environ, start_response)
370 finally:
370 finally:
371 # Dispose current database session and rollback uncommitted
371 # Dispose current database session and rollback uncommitted
372 # transactions.
372 # transactions.
373 meta.Session.remove()
373 meta.Session.remove()
374
374
375 # In a single threaded mode server, on non sqlite db we should have
375 # In a single threaded mode server, on non sqlite db we should have
376 # '0 Current Checked out connections' at the end of a request,
376 # '0 Current Checked out connections' at the end of a request,
377 # if not, then something, somewhere is leaving a connection open
377 # if not, then something, somewhere is leaving a connection open
378 pool = meta.Base.metadata.bind.engine.pool
378 pool = meta.Base.metadata.bind.engine.pool
379 log.debug('sa pool status: %s', pool.status())
379 log.debug('sa pool status: %s', pool.status())
380 log.debug('Request processing finalized')
380 log.debug('Request processing finalized')
381
381
382 return pyramid_app_with_cleanup
382 return pyramid_app_with_cleanup
383
383
384
384
385 def sanitize_settings_and_apply_defaults(global_config, settings):
385 def sanitize_settings_and_apply_defaults(global_config, settings):
386 """
386 """
387 Applies settings defaults and does all type conversion.
387 Applies settings defaults and does all type conversion.
388
388
389 We would move all settings parsing and preparation into this place, so that
389 We would move all settings parsing and preparation into this place, so that
390 we have only one place left which deals with this part. The remaining parts
390 we have only one place left which deals with this part. The remaining parts
391 of the application would start to rely fully on well prepared settings.
391 of the application would start to rely fully on well prepared settings.
392
392
393 This piece would later be split up per topic to avoid a big fat monster
393 This piece would later be split up per topic to avoid a big fat monster
394 function.
394 function.
395 """
395 """
396
396
397 settings.setdefault('rhodecode.edition', 'Community Edition')
397 settings.setdefault('rhodecode.edition', 'Community Edition')
398
398
399 if 'mako.default_filters' not in settings:
399 if 'mako.default_filters' not in settings:
400 # set custom default filters if we don't have it defined
400 # set custom default filters if we don't have it defined
401 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
401 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
402 settings['mako.default_filters'] = 'h_filter'
402 settings['mako.default_filters'] = 'h_filter'
403
403
404 if 'mako.directories' not in settings:
404 if 'mako.directories' not in settings:
405 mako_directories = settings.setdefault('mako.directories', [
405 mako_directories = settings.setdefault('mako.directories', [
406 # Base templates of the original application
406 # Base templates of the original application
407 'rhodecode:templates',
407 'rhodecode:templates',
408 ])
408 ])
409 log.debug(
409 log.debug(
410 "Using the following Mako template directories: %s",
410 "Using the following Mako template directories: %s",
411 mako_directories)
411 mako_directories)
412
412
413 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
414 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
415 raw_url = settings['beaker.session.url']
416 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
417 settings['beaker.session.url'] = 'redis://' + raw_url
418
413 # Default includes, possible to change as a user
419 # Default includes, possible to change as a user
414 pyramid_includes = settings.setdefault('pyramid.includes', [
420 pyramid_includes = settings.setdefault('pyramid.includes', [
415 'rhodecode.lib.middleware.request_wrapper',
421 'rhodecode.lib.middleware.request_wrapper',
416 ])
422 ])
417 log.debug(
423 log.debug(
418 "Using the following pyramid.includes: %s",
424 "Using the following pyramid.includes: %s",
419 pyramid_includes)
425 pyramid_includes)
420
426
421 # TODO: johbo: Re-think this, usually the call to config.include
427 # TODO: johbo: Re-think this, usually the call to config.include
422 # should allow to pass in a prefix.
428 # should allow to pass in a prefix.
423 settings.setdefault('rhodecode.api.url', '/_admin/api')
429 settings.setdefault('rhodecode.api.url', '/_admin/api')
424 settings.setdefault('__file__', global_config.get('__file__'))
430 settings.setdefault('__file__', global_config.get('__file__'))
425
431
426 # Sanitize generic settings.
432 # Sanitize generic settings.
427 _list_setting(settings, 'default_encoding', 'UTF-8')
433 _list_setting(settings, 'default_encoding', 'UTF-8')
428 _bool_setting(settings, 'is_test', 'false')
434 _bool_setting(settings, 'is_test', 'false')
429 _bool_setting(settings, 'gzip_responses', 'false')
435 _bool_setting(settings, 'gzip_responses', 'false')
430
436
431 # Call split out functions that sanitize settings for each topic.
437 # Call split out functions that sanitize settings for each topic.
432 _sanitize_appenlight_settings(settings)
438 _sanitize_appenlight_settings(settings)
433 _sanitize_vcs_settings(settings)
439 _sanitize_vcs_settings(settings)
434 _sanitize_cache_settings(settings)
440 _sanitize_cache_settings(settings)
435
441
436 # configure instance id
442 # configure instance id
437 config_utils.set_instance_id(settings)
443 config_utils.set_instance_id(settings)
438
444
439 return settings
445 return settings
440
446
441
447
442 def enable_debug():
448 def enable_debug():
443 """
449 """
444 Helper to enable debug on running instance
450 Helper to enable debug on running instance
445 :return:
451 :return:
446 """
452 """
447 import tempfile
453 import tempfile
448 import textwrap
454 import textwrap
449 import logging.config
455 import logging.config
450
456
451 ini_template = textwrap.dedent("""
457 ini_template = textwrap.dedent("""
452 #####################################
458 #####################################
453 ### DEBUG LOGGING CONFIGURATION ####
459 ### DEBUG LOGGING CONFIGURATION ####
454 #####################################
460 #####################################
455 [loggers]
461 [loggers]
456 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
462 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
457
463
458 [handlers]
464 [handlers]
459 keys = console, console_sql
465 keys = console, console_sql
460
466
461 [formatters]
467 [formatters]
462 keys = generic, color_formatter, color_formatter_sql
468 keys = generic, color_formatter, color_formatter_sql
463
469
464 #############
470 #############
465 ## LOGGERS ##
471 ## LOGGERS ##
466 #############
472 #############
467 [logger_root]
473 [logger_root]
468 level = NOTSET
474 level = NOTSET
469 handlers = console
475 handlers = console
470
476
471 [logger_sqlalchemy]
477 [logger_sqlalchemy]
472 level = INFO
478 level = INFO
473 handlers = console_sql
479 handlers = console_sql
474 qualname = sqlalchemy.engine
480 qualname = sqlalchemy.engine
475 propagate = 0
481 propagate = 0
476
482
477 [logger_beaker]
483 [logger_beaker]
478 level = DEBUG
484 level = DEBUG
479 handlers =
485 handlers =
480 qualname = beaker.container
486 qualname = beaker.container
481 propagate = 1
487 propagate = 1
482
488
483 [logger_rhodecode]
489 [logger_rhodecode]
484 level = DEBUG
490 level = DEBUG
485 handlers =
491 handlers =
486 qualname = rhodecode
492 qualname = rhodecode
487 propagate = 1
493 propagate = 1
488
494
489 [logger_ssh_wrapper]
495 [logger_ssh_wrapper]
490 level = DEBUG
496 level = DEBUG
491 handlers =
497 handlers =
492 qualname = ssh_wrapper
498 qualname = ssh_wrapper
493 propagate = 1
499 propagate = 1
494
500
495 [logger_celery]
501 [logger_celery]
496 level = DEBUG
502 level = DEBUG
497 handlers =
503 handlers =
498 qualname = celery
504 qualname = celery
499
505
500
506
501 ##############
507 ##############
502 ## HANDLERS ##
508 ## HANDLERS ##
503 ##############
509 ##############
504
510
505 [handler_console]
511 [handler_console]
506 class = StreamHandler
512 class = StreamHandler
507 args = (sys.stderr, )
513 args = (sys.stderr, )
508 level = DEBUG
514 level = DEBUG
509 formatter = color_formatter
515 formatter = color_formatter
510
516
511 [handler_console_sql]
517 [handler_console_sql]
512 # "level = DEBUG" logs SQL queries and results.
518 # "level = DEBUG" logs SQL queries and results.
513 # "level = INFO" logs SQL queries.
519 # "level = INFO" logs SQL queries.
514 # "level = WARN" logs neither. (Recommended for production systems.)
520 # "level = WARN" logs neither. (Recommended for production systems.)
515 class = StreamHandler
521 class = StreamHandler
516 args = (sys.stderr, )
522 args = (sys.stderr, )
517 level = WARN
523 level = WARN
518 formatter = color_formatter_sql
524 formatter = color_formatter_sql
519
525
520 ################
526 ################
521 ## FORMATTERS ##
527 ## FORMATTERS ##
522 ################
528 ################
523
529
524 [formatter_generic]
530 [formatter_generic]
525 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
531 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
526 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
532 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
527 datefmt = %Y-%m-%d %H:%M:%S
533 datefmt = %Y-%m-%d %H:%M:%S
528
534
529 [formatter_color_formatter]
535 [formatter_color_formatter]
530 class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter
536 class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter
531 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
537 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
532 datefmt = %Y-%m-%d %H:%M:%S
538 datefmt = %Y-%m-%d %H:%M:%S
533
539
534 [formatter_color_formatter_sql]
540 [formatter_color_formatter_sql]
535 class = rhodecode.lib.logging_formatter.ColorFormatterSql
541 class = rhodecode.lib.logging_formatter.ColorFormatterSql
536 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
542 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
537 datefmt = %Y-%m-%d %H:%M:%S
543 datefmt = %Y-%m-%d %H:%M:%S
538 """)
544 """)
539
545
540 with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini',
546 with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini',
541 delete=False) as f:
547 delete=False) as f:
542 log.info('Saved Temporary DEBUG config at %s', f.name)
548 log.info('Saved Temporary DEBUG config at %s', f.name)
543 f.write(ini_template)
549 f.write(ini_template)
544
550
545 logging.config.fileConfig(f.name)
551 logging.config.fileConfig(f.name)
546 log.debug('DEBUG MODE ON')
552 log.debug('DEBUG MODE ON')
547 os.remove(f.name)
553 os.remove(f.name)
548
554
549
555
550 def _sanitize_appenlight_settings(settings):
556 def _sanitize_appenlight_settings(settings):
551 _bool_setting(settings, 'appenlight', 'false')
557 _bool_setting(settings, 'appenlight', 'false')
552
558
553
559
554 def _sanitize_vcs_settings(settings):
560 def _sanitize_vcs_settings(settings):
555 """
561 """
556 Applies settings defaults and does type conversion for all VCS related
562 Applies settings defaults and does type conversion for all VCS related
557 settings.
563 settings.
558 """
564 """
559 _string_setting(settings, 'vcs.svn.compatible_version', '')
565 _string_setting(settings, 'vcs.svn.compatible_version', '')
560 _string_setting(settings, 'git_rev_filter', '--all')
566 _string_setting(settings, 'git_rev_filter', '--all')
561 _string_setting(settings, 'vcs.hooks.protocol', 'http')
567 _string_setting(settings, 'vcs.hooks.protocol', 'http')
562 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
568 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
563 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
569 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
564 _string_setting(settings, 'vcs.server', '')
570 _string_setting(settings, 'vcs.server', '')
565 _string_setting(settings, 'vcs.server.log_level', 'debug')
571 _string_setting(settings, 'vcs.server.log_level', 'debug')
566 _string_setting(settings, 'vcs.server.protocol', 'http')
572 _string_setting(settings, 'vcs.server.protocol', 'http')
567 _bool_setting(settings, 'startup.import_repos', 'false')
573 _bool_setting(settings, 'startup.import_repos', 'false')
568 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
574 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
569 _bool_setting(settings, 'vcs.server.enable', 'true')
575 _bool_setting(settings, 'vcs.server.enable', 'true')
570 _bool_setting(settings, 'vcs.start_server', 'false')
576 _bool_setting(settings, 'vcs.start_server', 'false')
571 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
577 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
572 _int_setting(settings, 'vcs.connection_timeout', 3600)
578 _int_setting(settings, 'vcs.connection_timeout', 3600)
573
579
574 # Support legacy values of vcs.scm_app_implementation. Legacy
580 # Support legacy values of vcs.scm_app_implementation. Legacy
575 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
581 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
576 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
582 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
577 scm_app_impl = settings['vcs.scm_app_implementation']
583 scm_app_impl = settings['vcs.scm_app_implementation']
578 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
584 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
579 settings['vcs.scm_app_implementation'] = 'http'
585 settings['vcs.scm_app_implementation'] = 'http'
580
586
581
587
582 def _sanitize_cache_settings(settings):
588 def _sanitize_cache_settings(settings):
583 temp_store = tempfile.gettempdir()
589 temp_store = tempfile.gettempdir()
584 default_cache_dir = os.path.join(temp_store, 'rc_cache')
590 default_cache_dir = os.path.join(temp_store, 'rc_cache')
585
591
586 # save default, cache dir, and use it for all backends later.
592 # save default, cache dir, and use it for all backends later.
587 default_cache_dir = _string_setting(
593 default_cache_dir = _string_setting(
588 settings,
594 settings,
589 'cache_dir',
595 'cache_dir',
590 default_cache_dir, lower=False, default_when_empty=True)
596 default_cache_dir, lower=False, default_when_empty=True)
591
597
592 # ensure we have our dir created
598 # ensure we have our dir created
593 if not os.path.isdir(default_cache_dir):
599 if not os.path.isdir(default_cache_dir):
594 os.makedirs(default_cache_dir, mode=0o755)
600 os.makedirs(default_cache_dir, mode=0o755)
595
601
596 # exception store cache
602 # exception store cache
597 _string_setting(
603 _string_setting(
598 settings,
604 settings,
599 'exception_tracker.store_path',
605 'exception_tracker.store_path',
600 temp_store, lower=False, default_when_empty=True)
606 temp_store, lower=False, default_when_empty=True)
601
607
602 # cache_perms
608 # cache_perms
603 _string_setting(
609 _string_setting(
604 settings,
610 settings,
605 'rc_cache.cache_perms.backend',
611 'rc_cache.cache_perms.backend',
606 'dogpile.cache.rc.file_namespace', lower=False)
612 'dogpile.cache.rc.file_namespace', lower=False)
607 _int_setting(
613 _int_setting(
608 settings,
614 settings,
609 'rc_cache.cache_perms.expiration_time',
615 'rc_cache.cache_perms.expiration_time',
610 60)
616 60)
611 _string_setting(
617 _string_setting(
612 settings,
618 settings,
613 'rc_cache.cache_perms.arguments.filename',
619 'rc_cache.cache_perms.arguments.filename',
614 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
620 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
615
621
616 # cache_repo
622 # cache_repo
617 _string_setting(
623 _string_setting(
618 settings,
624 settings,
619 'rc_cache.cache_repo.backend',
625 'rc_cache.cache_repo.backend',
620 'dogpile.cache.rc.file_namespace', lower=False)
626 'dogpile.cache.rc.file_namespace', lower=False)
621 _int_setting(
627 _int_setting(
622 settings,
628 settings,
623 'rc_cache.cache_repo.expiration_time',
629 'rc_cache.cache_repo.expiration_time',
624 60)
630 60)
625 _string_setting(
631 _string_setting(
626 settings,
632 settings,
627 'rc_cache.cache_repo.arguments.filename',
633 'rc_cache.cache_repo.arguments.filename',
628 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
634 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
629
635
630 # cache_license
636 # cache_license
631 _string_setting(
637 _string_setting(
632 settings,
638 settings,
633 'rc_cache.cache_license.backend',
639 'rc_cache.cache_license.backend',
634 'dogpile.cache.rc.file_namespace', lower=False)
640 'dogpile.cache.rc.file_namespace', lower=False)
635 _int_setting(
641 _int_setting(
636 settings,
642 settings,
637 'rc_cache.cache_license.expiration_time',
643 'rc_cache.cache_license.expiration_time',
638 5*60)
644 5*60)
639 _string_setting(
645 _string_setting(
640 settings,
646 settings,
641 'rc_cache.cache_license.arguments.filename',
647 'rc_cache.cache_license.arguments.filename',
642 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
648 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
643
649
644 # cache_repo_longterm memory, 96H
650 # cache_repo_longterm memory, 96H
645 _string_setting(
651 _string_setting(
646 settings,
652 settings,
647 'rc_cache.cache_repo_longterm.backend',
653 'rc_cache.cache_repo_longterm.backend',
648 'dogpile.cache.rc.memory_lru', lower=False)
654 'dogpile.cache.rc.memory_lru', lower=False)
649 _int_setting(
655 _int_setting(
650 settings,
656 settings,
651 'rc_cache.cache_repo_longterm.expiration_time',
657 'rc_cache.cache_repo_longterm.expiration_time',
652 345600)
658 345600)
653 _int_setting(
659 _int_setting(
654 settings,
660 settings,
655 'rc_cache.cache_repo_longterm.max_size',
661 'rc_cache.cache_repo_longterm.max_size',
656 10000)
662 10000)
657
663
658 # sql_cache_short
664 # sql_cache_short
659 _string_setting(
665 _string_setting(
660 settings,
666 settings,
661 'rc_cache.sql_cache_short.backend',
667 'rc_cache.sql_cache_short.backend',
662 'dogpile.cache.rc.memory_lru', lower=False)
668 'dogpile.cache.rc.memory_lru', lower=False)
663 _int_setting(
669 _int_setting(
664 settings,
670 settings,
665 'rc_cache.sql_cache_short.expiration_time',
671 'rc_cache.sql_cache_short.expiration_time',
666 30)
672 30)
667 _int_setting(
673 _int_setting(
668 settings,
674 settings,
669 'rc_cache.sql_cache_short.max_size',
675 'rc_cache.sql_cache_short.max_size',
670 10000)
676 10000)
671
677
672
678
673 def _int_setting(settings, name, default):
679 def _int_setting(settings, name, default):
674 settings[name] = int(settings.get(name, default))
680 settings[name] = int(settings.get(name, default))
675 return settings[name]
681 return settings[name]
676
682
677
683
678 def _bool_setting(settings, name, default):
684 def _bool_setting(settings, name, default):
679 input_val = settings.get(name, default)
685 input_val = settings.get(name, default)
680 if isinstance(input_val, unicode):
686 if isinstance(input_val, unicode):
681 input_val = input_val.encode('utf8')
687 input_val = input_val.encode('utf8')
682 settings[name] = asbool(input_val)
688 settings[name] = asbool(input_val)
683 return settings[name]
689 return settings[name]
684
690
685
691
686 def _list_setting(settings, name, default):
692 def _list_setting(settings, name, default):
687 raw_value = settings.get(name, default)
693 raw_value = settings.get(name, default)
688
694
689 old_separator = ','
695 old_separator = ','
690 if old_separator in raw_value:
696 if old_separator in raw_value:
691 # If we get a comma separated list, pass it to our own function.
697 # If we get a comma separated list, pass it to our own function.
692 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
698 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
693 else:
699 else:
694 # Otherwise we assume it uses pyramids space/newline separation.
700 # Otherwise we assume it uses pyramids space/newline separation.
695 settings[name] = aslist(raw_value)
701 settings[name] = aslist(raw_value)
696 return settings[name]
702 return settings[name]
697
703
698
704
699 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
705 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
700 value = settings.get(name, default)
706 value = settings.get(name, default)
701
707
702 if default_when_empty and not value:
708 if default_when_empty and not value:
703 # use default value when value is empty
709 # use default value when value is empty
704 value = default
710 value = default
705
711
706 if lower:
712 if lower:
707 value = value.lower()
713 value = value.lower()
708 settings[name] = value
714 settings[name] = value
709 return settings[name]
715 return settings[name]
710
716
711
717
712 def _substitute_values(mapping, substitutions):
718 def _substitute_values(mapping, substitutions):
713 result = {}
719 result = {}
714
720
715 try:
721 try:
716 for key, value in mapping.items():
722 for key, value in mapping.items():
717 # initialize without substitution first
723 # initialize without substitution first
718 result[key] = value
724 result[key] = value
719
725
720 # Note: Cannot use regular replacements, since they would clash
726 # Note: Cannot use regular replacements, since they would clash
721 # with the implementation of ConfigParser. Using "format" instead.
727 # with the implementation of ConfigParser. Using "format" instead.
722 try:
728 try:
723 result[key] = value.format(**substitutions)
729 result[key] = value.format(**substitutions)
724 except KeyError as e:
730 except KeyError as e:
725 env_var = '{}'.format(e.args[0])
731 env_var = '{}'.format(e.args[0])
726
732
727 msg = 'Failed to substitute: `{key}={{{var}}}` with environment entry. ' \
733 msg = 'Failed to substitute: `{key}={{{var}}}` with environment entry. ' \
728 'Make sure your environment has {var} set, or remove this ' \
734 'Make sure your environment has {var} set, or remove this ' \
729 'variable from config file'.format(key=key, var=env_var)
735 'variable from config file'.format(key=key, var=env_var)
730
736
731 if env_var.startswith('ENV_'):
737 if env_var.startswith('ENV_'):
732 raise ValueError(msg)
738 raise ValueError(msg)
733 else:
739 else:
734 log.warning(msg)
740 log.warning(msg)
735
741
736 except ValueError as e:
742 except ValueError as e:
737 log.warning('Failed to substitute ENV variable: %s', e)
743 log.warning('Failed to substitute ENV variable: %s', e)
738 result = mapping
744 result = mapping
739
745
740 return result
746 return result
General Comments 0
You need to be logged in to leave comments. Login now