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