##// END OF EJS Templates
error-pages: update message to more proper.
marcink -
r3321:ffd2b28a default
parent child Browse files
Show More
@@ -1,727 +1,727 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import sys
22 import sys
23 import logging
23 import logging
24 import collections
24 import collections
25 import tempfile
25 import tempfile
26 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(settings)
101 sanitize_settings_and_apply_defaults(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 += " Or you don't have permission to access it."
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('pyramid_beaker')
253 config.include('pyramid_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.login')
284 config.include('rhodecode.apps.login')
285 config.include('rhodecode.apps.home')
285 config.include('rhodecode.apps.home')
286 config.include('rhodecode.apps.journal')
286 config.include('rhodecode.apps.journal')
287 config.include('rhodecode.apps.repository')
287 config.include('rhodecode.apps.repository')
288 config.include('rhodecode.apps.repo_group')
288 config.include('rhodecode.apps.repo_group')
289 config.include('rhodecode.apps.user_group')
289 config.include('rhodecode.apps.user_group')
290 config.include('rhodecode.apps.search')
290 config.include('rhodecode.apps.search')
291 config.include('rhodecode.apps.user_profile')
291 config.include('rhodecode.apps.user_profile')
292 config.include('rhodecode.apps.user_group_profile')
292 config.include('rhodecode.apps.user_group_profile')
293 config.include('rhodecode.apps.my_account')
293 config.include('rhodecode.apps.my_account')
294 config.include('rhodecode.apps.svn_support')
294 config.include('rhodecode.apps.svn_support')
295 config.include('rhodecode.apps.ssh_support')
295 config.include('rhodecode.apps.ssh_support')
296 config.include('rhodecode.apps.gist')
296 config.include('rhodecode.apps.gist')
297 config.include('rhodecode.apps.debug_style')
297 config.include('rhodecode.apps.debug_style')
298 config.include('rhodecode.api')
298 config.include('rhodecode.api')
299
299
300 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
300 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
301 config.add_translation_dirs('rhodecode:i18n/')
301 config.add_translation_dirs('rhodecode:i18n/')
302 settings['default_locale_name'] = settings.get('lang', 'en')
302 settings['default_locale_name'] = settings.get('lang', 'en')
303
303
304 # Add subscribers.
304 # Add subscribers.
305 config.add_subscriber(inject_app_settings,
305 config.add_subscriber(inject_app_settings,
306 pyramid.events.ApplicationCreated)
306 pyramid.events.ApplicationCreated)
307 config.add_subscriber(scan_repositories_if_enabled,
307 config.add_subscriber(scan_repositories_if_enabled,
308 pyramid.events.ApplicationCreated)
308 pyramid.events.ApplicationCreated)
309 config.add_subscriber(write_metadata_if_needed,
309 config.add_subscriber(write_metadata_if_needed,
310 pyramid.events.ApplicationCreated)
310 pyramid.events.ApplicationCreated)
311 config.add_subscriber(write_js_routes_if_enabled,
311 config.add_subscriber(write_js_routes_if_enabled,
312 pyramid.events.ApplicationCreated)
312 pyramid.events.ApplicationCreated)
313
313
314 # request custom methods
314 # request custom methods
315 config.add_request_method(
315 config.add_request_method(
316 'rhodecode.lib.partial_renderer.get_partial_renderer',
316 'rhodecode.lib.partial_renderer.get_partial_renderer',
317 'get_partial_renderer')
317 'get_partial_renderer')
318
318
319 # Set the authorization policy.
319 # Set the authorization policy.
320 authz_policy = ACLAuthorizationPolicy()
320 authz_policy = ACLAuthorizationPolicy()
321 config.set_authorization_policy(authz_policy)
321 config.set_authorization_policy(authz_policy)
322
322
323 # Set the default renderer for HTML templates to mako.
323 # Set the default renderer for HTML templates to mako.
324 config.add_mako_renderer('.html')
324 config.add_mako_renderer('.html')
325
325
326 config.add_renderer(
326 config.add_renderer(
327 name='json_ext',
327 name='json_ext',
328 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
328 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
329
329
330 # include RhodeCode plugins
330 # include RhodeCode plugins
331 includes = aslist(settings.get('rhodecode.includes', []))
331 includes = aslist(settings.get('rhodecode.includes', []))
332 for inc in includes:
332 for inc in includes:
333 config.include(inc)
333 config.include(inc)
334
334
335 # custom not found view, if our pyramid app doesn't know how to handle
335 # custom not found view, if our pyramid app doesn't know how to handle
336 # the request pass it to potential VCS handling ap
336 # the request pass it to potential VCS handling ap
337 config.add_notfound_view(not_found_view)
337 config.add_notfound_view(not_found_view)
338 if not settings.get('debugtoolbar.enabled', False):
338 if not settings.get('debugtoolbar.enabled', False):
339 # disabled debugtoolbar handle all exceptions via the error_handlers
339 # disabled debugtoolbar handle all exceptions via the error_handlers
340 config.add_view(error_handler, context=Exception)
340 config.add_view(error_handler, context=Exception)
341
341
342 # all errors including 403/404/50X
342 # all errors including 403/404/50X
343 config.add_view(error_handler, context=HTTPError)
343 config.add_view(error_handler, context=HTTPError)
344
344
345
345
346 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
346 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
347 """
347 """
348 Apply outer WSGI middlewares around the application.
348 Apply outer WSGI middlewares around the application.
349 """
349 """
350 registry = config.registry
350 registry = config.registry
351 settings = registry.settings
351 settings = registry.settings
352
352
353 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
353 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
354 pyramid_app = HttpsFixup(pyramid_app, settings)
354 pyramid_app = HttpsFixup(pyramid_app, settings)
355
355
356 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
356 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
357 pyramid_app, settings)
357 pyramid_app, settings)
358 registry.ae_client = _ae_client
358 registry.ae_client = _ae_client
359
359
360 if settings['gzip_responses']:
360 if settings['gzip_responses']:
361 pyramid_app = make_gzip_middleware(
361 pyramid_app = make_gzip_middleware(
362 pyramid_app, settings, compress_level=1)
362 pyramid_app, settings, compress_level=1)
363
363
364 # this should be the outer most middleware in the wsgi stack since
364 # this should be the outer most middleware in the wsgi stack since
365 # middleware like Routes make database calls
365 # middleware like Routes make database calls
366 def pyramid_app_with_cleanup(environ, start_response):
366 def pyramid_app_with_cleanup(environ, start_response):
367 try:
367 try:
368 return pyramid_app(environ, start_response)
368 return pyramid_app(environ, start_response)
369 finally:
369 finally:
370 # Dispose current database session and rollback uncommitted
370 # Dispose current database session and rollback uncommitted
371 # transactions.
371 # transactions.
372 meta.Session.remove()
372 meta.Session.remove()
373
373
374 # In a single threaded mode server, on non sqlite db we should have
374 # In a single threaded mode server, on non sqlite db we should have
375 # '0 Current Checked out connections' at the end of a request,
375 # '0 Current Checked out connections' at the end of a request,
376 # if not, then something, somewhere is leaving a connection open
376 # if not, then something, somewhere is leaving a connection open
377 pool = meta.Base.metadata.bind.engine.pool
377 pool = meta.Base.metadata.bind.engine.pool
378 log.debug('sa pool status: %s', pool.status())
378 log.debug('sa pool status: %s', pool.status())
379 log.debug('Request processing finalized')
379 log.debug('Request processing finalized')
380
380
381 return pyramid_app_with_cleanup
381 return pyramid_app_with_cleanup
382
382
383
383
384 def sanitize_settings_and_apply_defaults(settings):
384 def sanitize_settings_and_apply_defaults(settings):
385 """
385 """
386 Applies settings defaults and does all type conversion.
386 Applies settings defaults and does all type conversion.
387
387
388 We would move all settings parsing and preparation into this place, so that
388 We would move all settings parsing and preparation into this place, so that
389 we have only one place left which deals with this part. The remaining parts
389 we have only one place left which deals with this part. The remaining parts
390 of the application would start to rely fully on well prepared settings.
390 of the application would start to rely fully on well prepared settings.
391
391
392 This piece would later be split up per topic to avoid a big fat monster
392 This piece would later be split up per topic to avoid a big fat monster
393 function.
393 function.
394 """
394 """
395
395
396 settings.setdefault('rhodecode.edition', 'Community Edition')
396 settings.setdefault('rhodecode.edition', 'Community Edition')
397
397
398 if 'mako.default_filters' not in settings:
398 if 'mako.default_filters' not in settings:
399 # set custom default filters if we don't have it defined
399 # set custom default filters if we don't have it defined
400 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
400 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
401 settings['mako.default_filters'] = 'h_filter'
401 settings['mako.default_filters'] = 'h_filter'
402
402
403 if 'mako.directories' not in settings:
403 if 'mako.directories' not in settings:
404 mako_directories = settings.setdefault('mako.directories', [
404 mako_directories = settings.setdefault('mako.directories', [
405 # Base templates of the original application
405 # Base templates of the original application
406 'rhodecode:templates',
406 'rhodecode:templates',
407 ])
407 ])
408 log.debug(
408 log.debug(
409 "Using the following Mako template directories: %s",
409 "Using the following Mako template directories: %s",
410 mako_directories)
410 mako_directories)
411
411
412 # Default includes, possible to change as a user
412 # Default includes, possible to change as a user
413 pyramid_includes = settings.setdefault('pyramid.includes', [
413 pyramid_includes = settings.setdefault('pyramid.includes', [
414 'rhodecode.lib.middleware.request_wrapper',
414 'rhodecode.lib.middleware.request_wrapper',
415 ])
415 ])
416 log.debug(
416 log.debug(
417 "Using the following pyramid.includes: %s",
417 "Using the following pyramid.includes: %s",
418 pyramid_includes)
418 pyramid_includes)
419
419
420 # TODO: johbo: Re-think this, usually the call to config.include
420 # TODO: johbo: Re-think this, usually the call to config.include
421 # should allow to pass in a prefix.
421 # should allow to pass in a prefix.
422 settings.setdefault('rhodecode.api.url', '/_admin/api')
422 settings.setdefault('rhodecode.api.url', '/_admin/api')
423
423
424 # Sanitize generic settings.
424 # Sanitize generic settings.
425 _list_setting(settings, 'default_encoding', 'UTF-8')
425 _list_setting(settings, 'default_encoding', 'UTF-8')
426 _bool_setting(settings, 'is_test', 'false')
426 _bool_setting(settings, 'is_test', 'false')
427 _bool_setting(settings, 'gzip_responses', 'false')
427 _bool_setting(settings, 'gzip_responses', 'false')
428
428
429 # Call split out functions that sanitize settings for each topic.
429 # Call split out functions that sanitize settings for each topic.
430 _sanitize_appenlight_settings(settings)
430 _sanitize_appenlight_settings(settings)
431 _sanitize_vcs_settings(settings)
431 _sanitize_vcs_settings(settings)
432 _sanitize_cache_settings(settings)
432 _sanitize_cache_settings(settings)
433
433
434 # configure instance id
434 # configure instance id
435 config_utils.set_instance_id(settings)
435 config_utils.set_instance_id(settings)
436
436
437 return settings
437 return settings
438
438
439
439
440 def enable_debug():
440 def enable_debug():
441 """
441 """
442 Helper to enable debug on running instance
442 Helper to enable debug on running instance
443 :return:
443 :return:
444 """
444 """
445 import tempfile
445 import tempfile
446 import textwrap
446 import textwrap
447 import logging.config
447 import logging.config
448
448
449 ini_template = textwrap.dedent("""
449 ini_template = textwrap.dedent("""
450 #####################################
450 #####################################
451 ### DEBUG LOGGING CONFIGURATION ####
451 ### DEBUG LOGGING CONFIGURATION ####
452 #####################################
452 #####################################
453 [loggers]
453 [loggers]
454 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
454 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
455
455
456 [handlers]
456 [handlers]
457 keys = console, console_sql
457 keys = console, console_sql
458
458
459 [formatters]
459 [formatters]
460 keys = generic, color_formatter, color_formatter_sql
460 keys = generic, color_formatter, color_formatter_sql
461
461
462 #############
462 #############
463 ## LOGGERS ##
463 ## LOGGERS ##
464 #############
464 #############
465 [logger_root]
465 [logger_root]
466 level = NOTSET
466 level = NOTSET
467 handlers = console
467 handlers = console
468
468
469 [logger_sqlalchemy]
469 [logger_sqlalchemy]
470 level = INFO
470 level = INFO
471 handlers = console_sql
471 handlers = console_sql
472 qualname = sqlalchemy.engine
472 qualname = sqlalchemy.engine
473 propagate = 0
473 propagate = 0
474
474
475 [logger_beaker]
475 [logger_beaker]
476 level = DEBUG
476 level = DEBUG
477 handlers =
477 handlers =
478 qualname = beaker.container
478 qualname = beaker.container
479 propagate = 1
479 propagate = 1
480
480
481 [logger_rhodecode]
481 [logger_rhodecode]
482 level = DEBUG
482 level = DEBUG
483 handlers =
483 handlers =
484 qualname = rhodecode
484 qualname = rhodecode
485 propagate = 1
485 propagate = 1
486
486
487 [logger_ssh_wrapper]
487 [logger_ssh_wrapper]
488 level = DEBUG
488 level = DEBUG
489 handlers =
489 handlers =
490 qualname = ssh_wrapper
490 qualname = ssh_wrapper
491 propagate = 1
491 propagate = 1
492
492
493 [logger_celery]
493 [logger_celery]
494 level = DEBUG
494 level = DEBUG
495 handlers =
495 handlers =
496 qualname = celery
496 qualname = celery
497
497
498
498
499 ##############
499 ##############
500 ## HANDLERS ##
500 ## HANDLERS ##
501 ##############
501 ##############
502
502
503 [handler_console]
503 [handler_console]
504 class = StreamHandler
504 class = StreamHandler
505 args = (sys.stderr, )
505 args = (sys.stderr, )
506 level = DEBUG
506 level = DEBUG
507 formatter = color_formatter
507 formatter = color_formatter
508
508
509 [handler_console_sql]
509 [handler_console_sql]
510 # "level = DEBUG" logs SQL queries and results.
510 # "level = DEBUG" logs SQL queries and results.
511 # "level = INFO" logs SQL queries.
511 # "level = INFO" logs SQL queries.
512 # "level = WARN" logs neither. (Recommended for production systems.)
512 # "level = WARN" logs neither. (Recommended for production systems.)
513 class = StreamHandler
513 class = StreamHandler
514 args = (sys.stderr, )
514 args = (sys.stderr, )
515 level = WARN
515 level = WARN
516 formatter = color_formatter_sql
516 formatter = color_formatter_sql
517
517
518 ################
518 ################
519 ## FORMATTERS ##
519 ## FORMATTERS ##
520 ################
520 ################
521
521
522 [formatter_generic]
522 [formatter_generic]
523 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
523 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
524 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
524 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
525 datefmt = %Y-%m-%d %H:%M:%S
525 datefmt = %Y-%m-%d %H:%M:%S
526
526
527 [formatter_color_formatter]
527 [formatter_color_formatter]
528 class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter
528 class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter
529 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
529 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
530 datefmt = %Y-%m-%d %H:%M:%S
530 datefmt = %Y-%m-%d %H:%M:%S
531
531
532 [formatter_color_formatter_sql]
532 [formatter_color_formatter_sql]
533 class = rhodecode.lib.logging_formatter.ColorFormatterSql
533 class = rhodecode.lib.logging_formatter.ColorFormatterSql
534 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
534 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
535 datefmt = %Y-%m-%d %H:%M:%S
535 datefmt = %Y-%m-%d %H:%M:%S
536 """)
536 """)
537
537
538 with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini',
538 with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini',
539 delete=False) as f:
539 delete=False) as f:
540 log.info('Saved Temporary DEBUG config at %s', f.name)
540 log.info('Saved Temporary DEBUG config at %s', f.name)
541 f.write(ini_template)
541 f.write(ini_template)
542
542
543 logging.config.fileConfig(f.name)
543 logging.config.fileConfig(f.name)
544 log.debug('DEBUG MODE ON')
544 log.debug('DEBUG MODE ON')
545 os.remove(f.name)
545 os.remove(f.name)
546
546
547
547
548 def _sanitize_appenlight_settings(settings):
548 def _sanitize_appenlight_settings(settings):
549 _bool_setting(settings, 'appenlight', 'false')
549 _bool_setting(settings, 'appenlight', 'false')
550
550
551
551
552 def _sanitize_vcs_settings(settings):
552 def _sanitize_vcs_settings(settings):
553 """
553 """
554 Applies settings defaults and does type conversion for all VCS related
554 Applies settings defaults and does type conversion for all VCS related
555 settings.
555 settings.
556 """
556 """
557 _string_setting(settings, 'vcs.svn.compatible_version', '')
557 _string_setting(settings, 'vcs.svn.compatible_version', '')
558 _string_setting(settings, 'git_rev_filter', '--all')
558 _string_setting(settings, 'git_rev_filter', '--all')
559 _string_setting(settings, 'vcs.hooks.protocol', 'http')
559 _string_setting(settings, 'vcs.hooks.protocol', 'http')
560 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
560 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
561 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
561 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
562 _string_setting(settings, 'vcs.server', '')
562 _string_setting(settings, 'vcs.server', '')
563 _string_setting(settings, 'vcs.server.log_level', 'debug')
563 _string_setting(settings, 'vcs.server.log_level', 'debug')
564 _string_setting(settings, 'vcs.server.protocol', 'http')
564 _string_setting(settings, 'vcs.server.protocol', 'http')
565 _bool_setting(settings, 'startup.import_repos', 'false')
565 _bool_setting(settings, 'startup.import_repos', 'false')
566 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
566 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
567 _bool_setting(settings, 'vcs.server.enable', 'true')
567 _bool_setting(settings, 'vcs.server.enable', 'true')
568 _bool_setting(settings, 'vcs.start_server', 'false')
568 _bool_setting(settings, 'vcs.start_server', 'false')
569 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
569 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
570 _int_setting(settings, 'vcs.connection_timeout', 3600)
570 _int_setting(settings, 'vcs.connection_timeout', 3600)
571
571
572 # Support legacy values of vcs.scm_app_implementation. Legacy
572 # Support legacy values of vcs.scm_app_implementation. Legacy
573 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
573 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
574 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
574 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
575 scm_app_impl = settings['vcs.scm_app_implementation']
575 scm_app_impl = settings['vcs.scm_app_implementation']
576 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
576 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
577 settings['vcs.scm_app_implementation'] = 'http'
577 settings['vcs.scm_app_implementation'] = 'http'
578
578
579
579
580 def _sanitize_cache_settings(settings):
580 def _sanitize_cache_settings(settings):
581 temp_store = tempfile.gettempdir()
581 temp_store = tempfile.gettempdir()
582 default_cache_dir = os.path.join(temp_store, 'rc_cache')
582 default_cache_dir = os.path.join(temp_store, 'rc_cache')
583
583
584 # save default, cache dir, and use it for all backends later.
584 # save default, cache dir, and use it for all backends later.
585 default_cache_dir = _string_setting(
585 default_cache_dir = _string_setting(
586 settings,
586 settings,
587 'cache_dir',
587 'cache_dir',
588 default_cache_dir, lower=False, default_when_empty=True)
588 default_cache_dir, lower=False, default_when_empty=True)
589
589
590 # ensure we have our dir created
590 # ensure we have our dir created
591 if not os.path.isdir(default_cache_dir):
591 if not os.path.isdir(default_cache_dir):
592 os.makedirs(default_cache_dir, mode=0o755)
592 os.makedirs(default_cache_dir, mode=0o755)
593
593
594 # exception store cache
594 # exception store cache
595 _string_setting(
595 _string_setting(
596 settings,
596 settings,
597 'exception_tracker.store_path',
597 'exception_tracker.store_path',
598 temp_store, lower=False, default_when_empty=True)
598 temp_store, lower=False, default_when_empty=True)
599
599
600 # cache_perms
600 # cache_perms
601 _string_setting(
601 _string_setting(
602 settings,
602 settings,
603 'rc_cache.cache_perms.backend',
603 'rc_cache.cache_perms.backend',
604 'dogpile.cache.rc.file_namespace', lower=False)
604 'dogpile.cache.rc.file_namespace', lower=False)
605 _int_setting(
605 _int_setting(
606 settings,
606 settings,
607 'rc_cache.cache_perms.expiration_time',
607 'rc_cache.cache_perms.expiration_time',
608 60)
608 60)
609 _string_setting(
609 _string_setting(
610 settings,
610 settings,
611 'rc_cache.cache_perms.arguments.filename',
611 'rc_cache.cache_perms.arguments.filename',
612 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
612 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
613
613
614 # cache_repo
614 # cache_repo
615 _string_setting(
615 _string_setting(
616 settings,
616 settings,
617 'rc_cache.cache_repo.backend',
617 'rc_cache.cache_repo.backend',
618 'dogpile.cache.rc.file_namespace', lower=False)
618 'dogpile.cache.rc.file_namespace', lower=False)
619 _int_setting(
619 _int_setting(
620 settings,
620 settings,
621 'rc_cache.cache_repo.expiration_time',
621 'rc_cache.cache_repo.expiration_time',
622 60)
622 60)
623 _string_setting(
623 _string_setting(
624 settings,
624 settings,
625 'rc_cache.cache_repo.arguments.filename',
625 'rc_cache.cache_repo.arguments.filename',
626 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
626 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
627
627
628 # cache_license
628 # cache_license
629 _string_setting(
629 _string_setting(
630 settings,
630 settings,
631 'rc_cache.cache_license.backend',
631 'rc_cache.cache_license.backend',
632 'dogpile.cache.rc.file_namespace', lower=False)
632 'dogpile.cache.rc.file_namespace', lower=False)
633 _int_setting(
633 _int_setting(
634 settings,
634 settings,
635 'rc_cache.cache_license.expiration_time',
635 'rc_cache.cache_license.expiration_time',
636 5*60)
636 5*60)
637 _string_setting(
637 _string_setting(
638 settings,
638 settings,
639 'rc_cache.cache_license.arguments.filename',
639 'rc_cache.cache_license.arguments.filename',
640 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
640 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
641
641
642 # cache_repo_longterm memory, 96H
642 # cache_repo_longterm memory, 96H
643 _string_setting(
643 _string_setting(
644 settings,
644 settings,
645 'rc_cache.cache_repo_longterm.backend',
645 'rc_cache.cache_repo_longterm.backend',
646 'dogpile.cache.rc.memory_lru', lower=False)
646 'dogpile.cache.rc.memory_lru', lower=False)
647 _int_setting(
647 _int_setting(
648 settings,
648 settings,
649 'rc_cache.cache_repo_longterm.expiration_time',
649 'rc_cache.cache_repo_longterm.expiration_time',
650 345600)
650 345600)
651 _int_setting(
651 _int_setting(
652 settings,
652 settings,
653 'rc_cache.cache_repo_longterm.max_size',
653 'rc_cache.cache_repo_longterm.max_size',
654 10000)
654 10000)
655
655
656 # sql_cache_short
656 # sql_cache_short
657 _string_setting(
657 _string_setting(
658 settings,
658 settings,
659 'rc_cache.sql_cache_short.backend',
659 'rc_cache.sql_cache_short.backend',
660 'dogpile.cache.rc.memory_lru', lower=False)
660 'dogpile.cache.rc.memory_lru', lower=False)
661 _int_setting(
661 _int_setting(
662 settings,
662 settings,
663 'rc_cache.sql_cache_short.expiration_time',
663 'rc_cache.sql_cache_short.expiration_time',
664 30)
664 30)
665 _int_setting(
665 _int_setting(
666 settings,
666 settings,
667 'rc_cache.sql_cache_short.max_size',
667 'rc_cache.sql_cache_short.max_size',
668 10000)
668 10000)
669
669
670
670
671 def _int_setting(settings, name, default):
671 def _int_setting(settings, name, default):
672 settings[name] = int(settings.get(name, default))
672 settings[name] = int(settings.get(name, default))
673 return settings[name]
673 return settings[name]
674
674
675
675
676 def _bool_setting(settings, name, default):
676 def _bool_setting(settings, name, default):
677 input_val = settings.get(name, default)
677 input_val = settings.get(name, default)
678 if isinstance(input_val, unicode):
678 if isinstance(input_val, unicode):
679 input_val = input_val.encode('utf8')
679 input_val = input_val.encode('utf8')
680 settings[name] = asbool(input_val)
680 settings[name] = asbool(input_val)
681 return settings[name]
681 return settings[name]
682
682
683
683
684 def _list_setting(settings, name, default):
684 def _list_setting(settings, name, default):
685 raw_value = settings.get(name, default)
685 raw_value = settings.get(name, default)
686
686
687 old_separator = ','
687 old_separator = ','
688 if old_separator in raw_value:
688 if old_separator in raw_value:
689 # If we get a comma separated list, pass it to our own function.
689 # If we get a comma separated list, pass it to our own function.
690 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
690 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
691 else:
691 else:
692 # Otherwise we assume it uses pyramids space/newline separation.
692 # Otherwise we assume it uses pyramids space/newline separation.
693 settings[name] = aslist(raw_value)
693 settings[name] = aslist(raw_value)
694 return settings[name]
694 return settings[name]
695
695
696
696
697 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
697 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
698 value = settings.get(name, default)
698 value = settings.get(name, default)
699
699
700 if default_when_empty and not value:
700 if default_when_empty and not value:
701 # use default value when value is empty
701 # use default value when value is empty
702 value = default
702 value = default
703
703
704 if lower:
704 if lower:
705 value = value.lower()
705 value = value.lower()
706 settings[name] = value
706 settings[name] = value
707 return settings[name]
707 return settings[name]
708
708
709
709
710 def _substitute_values(mapping, substitutions):
710 def _substitute_values(mapping, substitutions):
711
711
712 try:
712 try:
713 result = {
713 result = {
714 # Note: Cannot use regular replacements, since they would clash
714 # Note: Cannot use regular replacements, since they would clash
715 # with the implementation of ConfigParser. Using "format" instead.
715 # with the implementation of ConfigParser. Using "format" instead.
716 key: value.format(**substitutions)
716 key: value.format(**substitutions)
717 for key, value in mapping.items()
717 for key, value in mapping.items()
718 }
718 }
719 except KeyError as e:
719 except KeyError as e:
720 raise ValueError(
720 raise ValueError(
721 'Failed to substitute env variable: {}. '
721 'Failed to substitute env variable: {}. '
722 'Make sure you have specified this env variable without ENV_ prefix'.format(e))
722 'Make sure you have specified this env variable without ENV_ prefix'.format(e))
723 except ValueError as e:
723 except ValueError as e:
724 log.warning('Failed to substitute ENV variable: %s', e)
724 log.warning('Failed to substitute ENV variable: %s', e)
725 result = mapping
725 result = mapping
726
726
727 return result
727 return result
General Comments 0
You need to be logged in to leave comments. Login now