##// END OF EJS Templates
env-config: use proper ALL keys reconfiguration based on env vars
super-admin -
r4825:a78e628e default
parent child Browse files
Show More
@@ -1,66 +1,68 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-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 import os
21 21 from rhodecode.apps.file_store import config_keys
22 22 from rhodecode.config.settings_maker import SettingsMaker
23 23
24 24
25 25 def _sanitize_settings_and_apply_defaults(settings):
26 26 """
27 27 Set defaults, convert to python types and validate settings.
28 28 """
29 29 settings_maker = SettingsMaker(settings)
30 30
31 31 settings_maker.make_setting(config_keys.enabled, True, parser='bool')
32 32 settings_maker.make_setting(config_keys.backend, 'local')
33 33
34 34 default_store = os.path.join(os.path.dirname(settings['__file__']), 'upload_store')
35 35 settings_maker.make_setting(config_keys.store_path, default_store)
36 36
37 settings_maker.env_expand()
38
37 39
38 40 def includeme(config):
39 41 from rhodecode.apps.file_store.views import FileStoreView
40 42
41 43 settings = config.registry.settings
42 44 _sanitize_settings_and_apply_defaults(settings)
43 45
44 46 config.add_route(
45 47 name='upload_file',
46 48 pattern='/_file_store/upload')
47 49 config.add_view(
48 50 FileStoreView,
49 51 attr='upload_file',
50 52 route_name='upload_file', request_method='POST', renderer='json_ext')
51 53
52 54 config.add_route(
53 55 name='download_file',
54 56 pattern='/_file_store/download/{fid:.*}')
55 57 config.add_view(
56 58 FileStoreView,
57 59 attr='download_file',
58 60 route_name='download_file')
59 61
60 62 config.add_route(
61 63 name='download_file_by_token',
62 64 pattern='/_file_store/token-download/{_auth_token}/{fid:.*}')
63 65 config.add_view(
64 66 FileStoreView,
65 67 attr='download_file_by_token',
66 68 route_name='download_file_by_token')
@@ -1,59 +1,61 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-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 logging
22 22
23 23 from . import config_keys
24 24 from .events import SshKeyFileChangeEvent
25 25 from .subscribers import generate_ssh_authorized_keys_file_subscriber
26 26
27 27 from rhodecode.config.settings_maker import SettingsMaker
28 28
29 29 log = logging.getLogger(__name__)
30 30
31 31
32 32 def _sanitize_settings_and_apply_defaults(settings):
33 33 """
34 34 Set defaults, convert to python types and validate settings.
35 35 """
36 36 settings_maker = SettingsMaker(settings)
37 37
38 38 settings_maker.make_setting(config_keys.generate_authorized_keyfile, False, parser='bool')
39 39 settings_maker.make_setting(config_keys.wrapper_allow_shell, False, parser='bool')
40 40 settings_maker.make_setting(config_keys.enable_debug_logging, False, parser='bool')
41 41 settings_maker.make_setting(config_keys.ssh_key_generator_enabled, True, parser='bool')
42 42
43 43 settings_maker.make_setting(config_keys.authorized_keys_file_path, '~/.ssh/authorized_keys_rhodecode')
44 44 settings_maker.make_setting(config_keys.wrapper_cmd, '')
45 45 settings_maker.make_setting(config_keys.authorized_keys_line_ssh_opts, '')
46 46
47 47 settings_maker.make_setting(config_keys.ssh_hg_bin, '~/.rccontrol/vcsserver-1/profile/bin/hg')
48 48 settings_maker.make_setting(config_keys.ssh_git_bin, '~/.rccontrol/vcsserver-1/profile/bin/git')
49 49 settings_maker.make_setting(config_keys.ssh_svn_bin, '~/.rccontrol/vcsserver-1/profile/bin/svnserve')
50 50
51 settings_maker.env_expand()
52
51 53
52 54 def includeme(config):
53 55 settings = config.registry.settings
54 56 _sanitize_settings_and_apply_defaults(settings)
55 57
56 58 # if we have enable generation of file, subscribe to event
57 59 if settings[config_keys.generate_authorized_keyfile]:
58 60 config.add_subscriber(
59 61 generate_ssh_authorized_keys_file_subscriber, SshKeyFileChangeEvent)
@@ -1,89 +1,91 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-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 import os
21 21 import logging
22 22 from pyramid import compat
23 23
24 24 # Do not use `from rhodecode import events` here, it will be overridden by the
25 25 # events module in this package due to pythons import mechanism.
26 26 from rhodecode.events import RepoGroupEvent
27 27 from rhodecode.subscribers import AsyncSubprocessSubscriber
28 28 from rhodecode.config.settings_maker import SettingsMaker
29 29
30 30 from .events import ModDavSvnConfigChange
31 31 from .subscribers import generate_config_subscriber
32 32 from . import config_keys
33 33
34 34
35 35 log = logging.getLogger(__name__)
36 36
37 37
38 38 def _sanitize_settings_and_apply_defaults(settings):
39 39 """
40 40 Set defaults, convert to python types and validate settings.
41 41 """
42 42 settings_maker = SettingsMaker(settings)
43 43 settings_maker.make_setting(config_keys.generate_config, False, parser='bool')
44 44 settings_maker.make_setting(config_keys.list_parent_path, True, parser='bool')
45 45 settings_maker.make_setting(config_keys.reload_timeout, 10, parser='bool')
46 46 settings_maker.make_setting(config_keys.config_file_path, '')
47 47 settings_maker.make_setting(config_keys.location_root, '/')
48 48 settings_maker.make_setting(config_keys.reload_command, '')
49 49 settings_maker.make_setting(config_keys.template, '')
50 50
51 settings_maker.env_expand()
52
51 53 # Convert negative timeout values to zero.
52 54 if settings[config_keys.reload_timeout] < 0:
53 55 settings[config_keys.reload_timeout] = 0
54 56
55 57 # Append path separator to location root.
56 58 settings[config_keys.location_root] = _append_path_sep(
57 59 settings[config_keys.location_root])
58 60
59 61 # Validate settings.
60 62 if settings[config_keys.generate_config]:
61 63 assert len(settings[config_keys.config_file_path]) > 0
62 64
63 65
64 66 def _append_path_sep(path):
65 67 """
66 68 Append the path separator if missing.
67 69 """
68 70 if isinstance(path, compat.string_types) and not path.endswith(os.path.sep):
69 71 path += os.path.sep
70 72 return path
71 73
72 74
73 75 def includeme(config):
74 76 settings = config.registry.settings
75 77 _sanitize_settings_and_apply_defaults(settings)
76 78
77 79 if settings[config_keys.generate_config]:
78 80 # Add subscriber to generate the Apache mod dav svn configuration on
79 81 # repository group events.
80 82 config.add_subscriber(generate_config_subscriber, RepoGroupEvent)
81 83
82 84 # If a reload command is set add a subscriber to execute it on
83 85 # configuration changes.
84 86 reload_cmd = settings[config_keys.reload_command]
85 87 if reload_cmd:
86 88 reload_timeout = settings[config_keys.reload_timeout] or None
87 89 reload_subscriber = AsyncSubprocessSubscriber(
88 90 cmd=reload_cmd, timeout=reload_timeout)
89 91 config.add_subscriber(reload_subscriber, ModDavSvnConfigChange)
@@ -1,611 +1,614 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 collections
24 24 import tempfile
25 25 import time
26 26 import logging.config
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 41 from rhodecode.config.settings_maker import SettingsMaker
42 42 from rhodecode.config.environment import load_pyramid_environment
43 43
44 44 import rhodecode.events
45 45 from rhodecode.lib.middleware.vcs import VCSMiddleware
46 46 from rhodecode.lib.request import Request
47 47 from rhodecode.lib.vcs import VCSCommunicationError
48 48 from rhodecode.lib.exceptions import VCSServerUnavailable
49 49 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
50 50 from rhodecode.lib.middleware.https_fixup import HttpsFixup
51 51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
52 52 from rhodecode.lib.utils2 import AttributeDict
53 53 from rhodecode.lib.exc_tracking import store_exception
54 54 from rhodecode.subscribers import (
55 55 scan_repositories_if_enabled, write_js_routes_if_enabled,
56 56 write_metadata_if_needed, write_usage_data)
57 57 from rhodecode.lib.statsd_client import StatsdClient
58 58
59 59 log = logging.getLogger(__name__)
60 60
61 61
62 62 def is_http_error(response):
63 63 # error which should have traceback
64 64 return response.status_code > 499
65 65
66 66
67 67 def should_load_all():
68 68 """
69 69 Returns if all application components should be loaded. In some cases it's
70 70 desired to skip apps loading for faster shell script execution
71 71 """
72 72 ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER')
73 73 if ssh_cmd:
74 74 return False
75 75
76 76 return True
77 77
78 78
79 79 def make_pyramid_app(global_config, **settings):
80 80 """
81 81 Constructs the WSGI application based on Pyramid.
82 82
83 83 Specials:
84 84
85 85 * The application can also be integrated like a plugin via the call to
86 86 `includeme`. This is accompanied with the other utility functions which
87 87 are called. Changing this should be done with great care to not break
88 88 cases when these fragments are assembled from another place.
89 89
90 90 """
91 91 start_time = time.time()
92 92 log.info('Pyramid app config starting')
93 93
94 94 sanitize_settings_and_apply_defaults(global_config, settings)
95 95
96 96 # init and bootstrap StatsdClient
97 97 StatsdClient.setup(settings)
98 98
99 99 config = Configurator(settings=settings)
100 100 # Init our statsd at very start
101 101 config.registry.statsd = StatsdClient.statsd
102 102
103 103 # Apply compatibility patches
104 104 patches.inspect_getargspec()
105 105
106 106 load_pyramid_environment(global_config, settings)
107 107
108 108 # Static file view comes first
109 109 includeme_first(config)
110 110
111 111 includeme(config)
112 112
113 113 pyramid_app = config.make_wsgi_app()
114 114 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
115 115 pyramid_app.config = config
116 116
117 117 celery_settings = get_celery_config(settings)
118 118 config.configure_celery(celery_settings)
119 119
120 120 # creating the app uses a connection - return it after we are done
121 121 meta.Session.remove()
122 122
123 123 total_time = time.time() - start_time
124 124 log.info('Pyramid app `%s` created and configured in %.2fs',
125 125 getattr(pyramid_app, 'func_name', 'pyramid_app'), total_time)
126 126 return pyramid_app
127 127
128 128
129 129 def get_celery_config(settings):
130 130 """
131 131 Converts basic ini configuration into celery 4.X options
132 132 """
133 133
134 134 def key_converter(key_name):
135 135 pref = 'celery.'
136 136 if key_name.startswith(pref):
137 137 return key_name[len(pref):].replace('.', '_').lower()
138 138
139 139 def type_converter(parsed_key, value):
140 140 # cast to int
141 141 if value.isdigit():
142 142 return int(value)
143 143
144 144 # cast to bool
145 145 if value.lower() in ['true', 'false', 'True', 'False']:
146 146 return value.lower() == 'true'
147 147 return value
148 148
149 149 celery_config = {}
150 150 for k, v in settings.items():
151 151 pref = 'celery.'
152 152 if k.startswith(pref):
153 153 celery_config[key_converter(k)] = type_converter(key_converter(k), v)
154 154
155 155 # TODO:rethink if we want to support celerybeat based file config, probably NOT
156 156 # beat_config = {}
157 157 # for section in parser.sections():
158 158 # if section.startswith('celerybeat:'):
159 159 # name = section.split(':', 1)[1]
160 160 # beat_config[name] = get_beat_config(parser, section)
161 161
162 162 # final compose of settings
163 163 celery_settings = {}
164 164
165 165 if celery_config:
166 166 celery_settings.update(celery_config)
167 167 # if beat_config:
168 168 # celery_settings.update({'beat_schedule': beat_config})
169 169
170 170 return celery_settings
171 171
172 172
173 173 def not_found_view(request):
174 174 """
175 175 This creates the view which should be registered as not-found-view to
176 176 pyramid.
177 177 """
178 178
179 179 if not getattr(request, 'vcs_call', None):
180 180 # handle like regular case with our error_handler
181 181 return error_handler(HTTPNotFound(), request)
182 182
183 183 # handle not found view as a vcs call
184 184 settings = request.registry.settings
185 185 ae_client = getattr(request, 'ae_client', None)
186 186 vcs_app = VCSMiddleware(
187 187 HTTPNotFound(), request.registry, settings,
188 188 appenlight_client=ae_client)
189 189
190 190 return wsgiapp(vcs_app)(None, request)
191 191
192 192
193 193 def error_handler(exception, request):
194 194 import rhodecode
195 195 from rhodecode.lib import helpers
196 196 from rhodecode.lib.utils2 import str2bool
197 197
198 198 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
199 199
200 200 base_response = HTTPInternalServerError()
201 201 # prefer original exception for the response since it may have headers set
202 202 if isinstance(exception, HTTPException):
203 203 base_response = exception
204 204 elif isinstance(exception, VCSCommunicationError):
205 205 base_response = VCSServerUnavailable()
206 206
207 207 if is_http_error(base_response):
208 208 log.exception(
209 209 'error occurred handling this request for path: %s', request.path)
210 210
211 211 error_explanation = base_response.explanation or str(base_response)
212 212 if base_response.status_code == 404:
213 213 error_explanation += " Optionally you don't have permission to access this page."
214 214 c = AttributeDict()
215 215 c.error_message = base_response.status
216 216 c.error_explanation = error_explanation
217 217 c.visual = AttributeDict()
218 218
219 219 c.visual.rhodecode_support_url = (
220 220 request.registry.settings.get('rhodecode_support_url') or
221 221 request.route_url('rhodecode_support')
222 222 )
223 223 c.redirect_time = 0
224 224 c.rhodecode_name = rhodecode_title
225 225 if not c.rhodecode_name:
226 226 c.rhodecode_name = 'Rhodecode'
227 227
228 228 c.causes = []
229 229 if is_http_error(base_response):
230 230 c.causes.append('Server is overloaded.')
231 231 c.causes.append('Server database connection is lost.')
232 232 c.causes.append('Server expected unhandled error.')
233 233
234 234 if hasattr(base_response, 'causes'):
235 235 c.causes = base_response.causes
236 236
237 237 c.messages = helpers.flash.pop_messages(request=request)
238 238
239 239 exc_info = sys.exc_info()
240 240 c.exception_id = id(exc_info)
241 241 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
242 242 or base_response.status_code > 499
243 243 c.exception_id_url = request.route_url(
244 244 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
245 245
246 246 if c.show_exception_id:
247 247 store_exception(c.exception_id, exc_info)
248 248 c.exception_debug = str2bool(rhodecode.CONFIG.get('debug'))
249 249 c.exception_config_ini = rhodecode.CONFIG.get('__file__')
250 250
251 251 response = render_to_response(
252 252 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
253 253 response=base_response)
254 254
255 255 statsd = request.registry.statsd
256 256 if statsd and base_response.status_code > 499:
257 257 exc_type = "{}.{}".format(exception.__class__.__module__, exception.__class__.__name__)
258 258 statsd.incr('rhodecode_exception_total',
259 259 tags=["exc_source:web",
260 260 "http_code:{}".format(base_response.status_code),
261 261 "type:{}".format(exc_type)])
262 262
263 263 return response
264 264
265 265
266 266 def includeme_first(config):
267 267 # redirect automatic browser favicon.ico requests to correct place
268 268 def favicon_redirect(context, request):
269 269 return HTTPFound(
270 270 request.static_path('rhodecode:public/images/favicon.ico'))
271 271
272 272 config.add_view(favicon_redirect, route_name='favicon')
273 273 config.add_route('favicon', '/favicon.ico')
274 274
275 275 def robots_redirect(context, request):
276 276 return HTTPFound(
277 277 request.static_path('rhodecode:public/robots.txt'))
278 278
279 279 config.add_view(robots_redirect, route_name='robots')
280 280 config.add_route('robots', '/robots.txt')
281 281
282 282 config.add_static_view(
283 283 '_static/deform', 'deform:static')
284 284 config.add_static_view(
285 285 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
286 286
287 287
288 288 def includeme(config, auth_resources=None):
289 289 from rhodecode.lib.celerylib.loader import configure_celery
290 290 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
291 291 settings = config.registry.settings
292 292 config.set_request_factory(Request)
293 293
294 294 # plugin information
295 295 config.registry.rhodecode_plugins = collections.OrderedDict()
296 296
297 297 config.add_directive(
298 298 'register_rhodecode_plugin', register_rhodecode_plugin)
299 299
300 300 config.add_directive('configure_celery', configure_celery)
301 301
302 302 if settings.get('appenlight', False):
303 303 config.include('appenlight_client.ext.pyramid_tween')
304 304
305 305 load_all = should_load_all()
306 306
307 307 # Includes which are required. The application would fail without them.
308 308 config.include('pyramid_mako')
309 309 config.include('rhodecode.lib.rc_beaker')
310 310 config.include('rhodecode.lib.rc_cache')
311 311 config.include('rhodecode.apps._base.navigation')
312 312 config.include('rhodecode.apps._base.subscribers')
313 313 config.include('rhodecode.tweens')
314 314 config.include('rhodecode.authentication')
315 315
316 316 if load_all:
317 317 ce_auth_resources = [
318 318 'rhodecode.authentication.plugins.auth_crowd',
319 319 'rhodecode.authentication.plugins.auth_headers',
320 320 'rhodecode.authentication.plugins.auth_jasig_cas',
321 321 'rhodecode.authentication.plugins.auth_ldap',
322 322 'rhodecode.authentication.plugins.auth_pam',
323 323 'rhodecode.authentication.plugins.auth_rhodecode',
324 324 'rhodecode.authentication.plugins.auth_token',
325 325 ]
326 326
327 327 # load CE authentication plugins
328 328
329 329 if auth_resources:
330 330 ce_auth_resources.extend(auth_resources)
331 331
332 332 for resource in ce_auth_resources:
333 333 config.include(resource)
334 334
335 335 # Auto discover authentication plugins and include their configuration.
336 336 if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')):
337 337 from rhodecode.authentication import discover_legacy_plugins
338 338 discover_legacy_plugins(config)
339 339
340 340 # apps
341 341 if load_all:
342 342 config.include('rhodecode.api')
343 343 config.include('rhodecode.apps._base')
344 344 config.include('rhodecode.apps.hovercards')
345 345 config.include('rhodecode.apps.ops')
346 346 config.include('rhodecode.apps.channelstream')
347 347 config.include('rhodecode.apps.file_store')
348 348 config.include('rhodecode.apps.admin')
349 349 config.include('rhodecode.apps.login')
350 350 config.include('rhodecode.apps.home')
351 351 config.include('rhodecode.apps.journal')
352 352
353 353 config.include('rhodecode.apps.repository')
354 354 config.include('rhodecode.apps.repo_group')
355 355 config.include('rhodecode.apps.user_group')
356 356 config.include('rhodecode.apps.search')
357 357 config.include('rhodecode.apps.user_profile')
358 358 config.include('rhodecode.apps.user_group_profile')
359 359 config.include('rhodecode.apps.my_account')
360 360 config.include('rhodecode.apps.gist')
361 361
362 362 config.include('rhodecode.apps.svn_support')
363 363 config.include('rhodecode.apps.ssh_support')
364 364 config.include('rhodecode.apps.debug_style')
365 365
366 366 if load_all:
367 367 config.include('rhodecode.integrations')
368 368
369 369 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
370 370 config.add_translation_dirs('rhodecode:i18n/')
371 371 settings['default_locale_name'] = settings.get('lang', 'en')
372 372
373 373 # Add subscribers.
374 374 if load_all:
375 375 config.add_subscriber(scan_repositories_if_enabled,
376 376 pyramid.events.ApplicationCreated)
377 377 config.add_subscriber(write_metadata_if_needed,
378 378 pyramid.events.ApplicationCreated)
379 379 config.add_subscriber(write_usage_data,
380 380 pyramid.events.ApplicationCreated)
381 381 config.add_subscriber(write_js_routes_if_enabled,
382 382 pyramid.events.ApplicationCreated)
383 383
384 384 # request custom methods
385 385 config.add_request_method(
386 386 'rhodecode.lib.partial_renderer.get_partial_renderer',
387 387 'get_partial_renderer')
388 388
389 389 config.add_request_method(
390 390 'rhodecode.lib.request_counter.get_request_counter',
391 391 'request_count')
392 392
393 393 # Set the authorization policy.
394 394 authz_policy = ACLAuthorizationPolicy()
395 395 config.set_authorization_policy(authz_policy)
396 396
397 397 # Set the default renderer for HTML templates to mako.
398 398 config.add_mako_renderer('.html')
399 399
400 400 config.add_renderer(
401 401 name='json_ext',
402 402 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
403 403
404 404 config.add_renderer(
405 405 name='string_html',
406 406 factory='rhodecode.lib.string_renderer.html')
407 407
408 408 # include RhodeCode plugins
409 409 includes = aslist(settings.get('rhodecode.includes', []))
410 410 for inc in includes:
411 411 config.include(inc)
412 412
413 413 # custom not found view, if our pyramid app doesn't know how to handle
414 414 # the request pass it to potential VCS handling ap
415 415 config.add_notfound_view(not_found_view)
416 416 if not settings.get('debugtoolbar.enabled', False):
417 417 # disabled debugtoolbar handle all exceptions via the error_handlers
418 418 config.add_view(error_handler, context=Exception)
419 419
420 420 # all errors including 403/404/50X
421 421 config.add_view(error_handler, context=HTTPError)
422 422
423 423
424 424 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
425 425 """
426 426 Apply outer WSGI middlewares around the application.
427 427 """
428 428 registry = config.registry
429 429 settings = registry.settings
430 430
431 431 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
432 432 pyramid_app = HttpsFixup(pyramid_app, settings)
433 433
434 434 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
435 435 pyramid_app, settings)
436 436 registry.ae_client = _ae_client
437 437
438 438 if settings['gzip_responses']:
439 439 pyramid_app = make_gzip_middleware(
440 440 pyramid_app, settings, compress_level=1)
441 441
442 442 # this should be the outer most middleware in the wsgi stack since
443 443 # middleware like Routes make database calls
444 444 def pyramid_app_with_cleanup(environ, start_response):
445 445 try:
446 446 return pyramid_app(environ, start_response)
447 447 finally:
448 448 # Dispose current database session and rollback uncommitted
449 449 # transactions.
450 450 meta.Session.remove()
451 451
452 452 # In a single threaded mode server, on non sqlite db we should have
453 453 # '0 Current Checked out connections' at the end of a request,
454 454 # if not, then something, somewhere is leaving a connection open
455 455 pool = meta.Base.metadata.bind.engine.pool
456 456 log.debug('sa pool status: %s', pool.status())
457 457 log.debug('Request processing finalized')
458 458
459 459 return pyramid_app_with_cleanup
460 460
461 461
462 462 def sanitize_settings_and_apply_defaults(global_config, settings):
463 463 """
464 464 Applies settings defaults and does all type conversion.
465 465
466 466 We would move all settings parsing and preparation into this place, so that
467 467 we have only one place left which deals with this part. The remaining parts
468 468 of the application would start to rely fully on well prepared settings.
469 469
470 470 This piece would later be split up per topic to avoid a big fat monster
471 471 function.
472 472 """
473 473
474 474 global_settings_maker = SettingsMaker(global_config)
475 475 global_settings_maker.make_setting('debug', default=False, parser='bool')
476 476 debug_enabled = asbool(global_config.get('debug'))
477 477
478 478 settings_maker = SettingsMaker(settings)
479 479
480 480 settings_maker.make_setting(
481 481 'logging.autoconfigure',
482 482 default=True,
483 483 parser='bool')
484 484
485 485 logging_conf = os.path.join(os.path.dirname(global_config.get('__file__')), 'logging.ini')
486 486 settings_maker.enable_logging(logging_conf, level='INFO' if debug_enabled else 'DEBUG')
487 487
488 488 # Default includes, possible to change as a user
489 489 pyramid_includes = settings_maker.make_setting('pyramid.includes', [], parser='list:newline')
490 490 log.debug(
491 491 "Using the following pyramid.includes: %s",
492 492 pyramid_includes)
493 493
494 494 settings_maker.make_setting('rhodecode.edition', 'Community Edition')
495 495 settings_maker.make_setting('rhodecode.edition_id', 'CE')
496 496
497 497 if 'mako.default_filters' not in settings:
498 498 # set custom default filters if we don't have it defined
499 499 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
500 500 settings['mako.default_filters'] = 'h_filter'
501 501
502 502 if 'mako.directories' not in settings:
503 503 mako_directories = settings.setdefault('mako.directories', [
504 504 # Base templates of the original application
505 505 'rhodecode:templates',
506 506 ])
507 507 log.debug(
508 508 "Using the following Mako template directories: %s",
509 509 mako_directories)
510 510
511 511 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
512 512 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
513 513 raw_url = settings['beaker.session.url']
514 514 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
515 515 settings['beaker.session.url'] = 'redis://' + raw_url
516 516
517 517 settings_maker.make_setting('__file__', global_config.get('__file__'))
518 518
519 519 # TODO: johbo: Re-think this, usually the call to config.include
520 520 # should allow to pass in a prefix.
521 521 settings_maker.make_setting('rhodecode.api.url', '/_admin/api')
522 522
523 523 # Sanitize generic settings.
524 524 settings_maker.make_setting('default_encoding', 'UTF-8', parser='list')
525 525 settings_maker.make_setting('is_test', False, parser='bool')
526 526 settings_maker.make_setting('gzip_responses', False, parser='bool')
527 527
528 # statsd
529 settings_maker.make_setting('statsd.enabled', False, parser='bool')
530 settings_maker.make_setting('statsd.statsd_host', '0.0.0.0')
531 settings_maker.make_setting('statsd.statsd_port', 8125, parser='int')
532 settings_maker.make_setting('statsd.statsd_prefix', '')
533 settings_maker.make_setting('statsd.statsd_ipv6', False, parser='bool')
534
528 535 settings_maker.make_setting('vcs.svn.compatible_version', '')
529 536 settings_maker.make_setting('vcs.hooks.protocol', 'http')
530 537 settings_maker.make_setting('vcs.hooks.host', '127.0.0.1')
531 538 settings_maker.make_setting('vcs.scm_app_implementation', 'http')
532 539 settings_maker.make_setting('vcs.server', '')
533 540 settings_maker.make_setting('vcs.server.protocol', 'http')
534 541 settings_maker.make_setting('startup.import_repos', 'false', parser='bool')
535 542 settings_maker.make_setting('vcs.hooks.direct_calls', 'false', parser='bool')
536 543 settings_maker.make_setting('vcs.server.enable', 'true', parser='bool')
537 544 settings_maker.make_setting('vcs.start_server', 'false', parser='bool')
538 545 settings_maker.make_setting('vcs.backends', 'hg, git, svn', parser='list')
539 546 settings_maker.make_setting('vcs.connection_timeout', 3600, parser='int')
540 547
541 548 settings_maker.make_setting('vcs.methods.cache', True, parser='bool')
542 549
543 550 # Support legacy values of vcs.scm_app_implementation. Legacy
544 551 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
545 552 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
546 553 scm_app_impl = settings['vcs.scm_app_implementation']
547 554 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
548 555 settings['vcs.scm_app_implementation'] = 'http'
549 556
550 557 settings_maker.make_setting('appenlight', False, parser='bool')
551 558
552 _sanitize_cache_settings(settings)
553
554 # configure instance id
555 config_utils.set_instance_id(settings)
556
557 return settings
558
559
560 def _sanitize_cache_settings(settings):
561 settings_maker = SettingsMaker(settings)
562
563 559 temp_store = tempfile.gettempdir()
564 560 default_cache_dir = os.path.join(temp_store, 'rc_cache')
565 561
566 562 # save default, cache dir, and use it for all backends later.
567 563 default_cache_dir = settings_maker.make_setting(
568 564 'cache_dir',
569 565 default=default_cache_dir, default_when_empty=True,
570 566 parser='dir:ensured')
571 567
572 568 # exception store cache
573 569 settings_maker.make_setting(
574 570 'exception_tracker.store_path',
575 571 default=os.path.join(default_cache_dir, 'exc_store'), default_when_empty=True,
576 572 parser='dir:ensured'
577 573 )
578 574
579 575 settings_maker.make_setting(
580 576 'celerybeat-schedule.path',
581 577 default=os.path.join(default_cache_dir, 'celerybeat_schedule', 'celerybeat-schedule.db'), default_when_empty=True,
582 578 parser='file:ensured'
583 579 )
584 580
585 581 settings_maker.make_setting('exception_tracker.send_email', False, parser='bool')
586 582 settings_maker.make_setting('exception_tracker.email_prefix', '[RHODECODE ERROR]', default_when_empty=True)
587 583
588 584 # cache_perms
589 585 settings_maker.make_setting('rc_cache.cache_perms.backend', 'dogpile.cache.rc.file_namespace')
590 586 settings_maker.make_setting('rc_cache.cache_perms.expiration_time', 60, parser='int')
591 587 settings_maker.make_setting('rc_cache.cache_perms.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_perms.db'))
592 588
593 589 # cache_repo
594 590 settings_maker.make_setting('rc_cache.cache_repo.backend', 'dogpile.cache.rc.file_namespace')
595 591 settings_maker.make_setting('rc_cache.cache_repo.expiration_time', 60, parser='int')
596 592 settings_maker.make_setting('rc_cache.cache_repo.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_repo.db'))
597 593
598 594 # cache_license
599 595 settings_maker.make_setting('rc_cache.cache_license.backend', 'dogpile.cache.rc.file_namespace')
600 596 settings_maker.make_setting('rc_cache.cache_license.expiration_time', 5*60, parser='int')
601 597 settings_maker.make_setting('rc_cache.cache_license.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_license.db'))
602 598
603 599 # cache_repo_longterm memory, 96H
604 600 settings_maker.make_setting('rc_cache.cache_repo_longterm.backend', 'dogpile.cache.rc.memory_lru')
605 601 settings_maker.make_setting('rc_cache.cache_repo_longterm.expiration_time', 345600, parser='int')
606 602 settings_maker.make_setting('rc_cache.cache_repo_longterm.max_size', 10000, parser='int')
607 603
608 604 # sql_cache_short
609 605 settings_maker.make_setting('rc_cache.sql_cache_short.backend', 'dogpile.cache.rc.memory_lru')
610 606 settings_maker.make_setting('rc_cache.sql_cache_short.expiration_time', 30, parser='int')
611 607 settings_maker.make_setting('rc_cache.sql_cache_short.max_size', 10000, parser='int')
608
609 settings_maker.env_expand()
610
611 # configure instance id
612 config_utils.set_instance_id(settings)
613
614 return settings
@@ -1,182 +1,204 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 textwrap
23 23 import string
24 24 import functools
25 25 import logging
26 26 import tempfile
27 27 import logging.config
28 28 log = logging.getLogger(__name__)
29 29
30 set_keys = {}
31
30 32
31 33 def str2bool(_str):
32 34 """
33 35 returns True/False value from given string, it tries to translate the
34 36 string into boolean
35 37
36 38 :param _str: string value to translate into boolean
37 39 :rtype: boolean
38 40 :returns: boolean from given string
39 41 """
40 42 if _str is None:
41 43 return False
42 44 if _str in (True, False):
43 45 return _str
44 46 _str = str(_str).strip().lower()
45 47 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
46 48
47 49
48 50 def aslist(obj, sep=None, strip=True):
49 51 """
50 52 Returns given string separated by sep as list
51 53
52 54 :param obj:
53 55 :param sep:
54 56 :param strip:
55 57 """
56 58 if isinstance(obj, (basestring,)):
57 59 if obj in ['', ""]:
58 60 return []
59 61
60 62 lst = obj.split(sep)
61 63 if strip:
62 64 lst = [v.strip() for v in lst]
63 65 return lst
64 66 elif isinstance(obj, (list, tuple)):
65 67 return obj
66 68 elif obj is None:
67 69 return []
68 70 else:
69 71 return [obj]
70 72
71 73
72 74 class SettingsMaker(object):
73 75
74 76 def __init__(self, app_settings):
75 77 self.settings = app_settings
76 78
77 79 @classmethod
78 80 def _bool_func(cls, input_val):
79 81 if isinstance(input_val, unicode):
80 82 input_val = input_val.encode('utf8')
81 83 return str2bool(input_val)
82 84
83 85 @classmethod
84 86 def _int_func(cls, input_val):
85 87 return int(input_val)
86 88
87 89 @classmethod
88 90 def _list_func(cls, input_val, sep=','):
89 91 return aslist(input_val, sep=sep)
90 92
91 93 @classmethod
92 94 def _string_func(cls, input_val, lower=True):
93 95 if lower:
94 96 input_val = input_val.lower()
95 97 return input_val
96 98
97 99 @classmethod
98 100 def _float_func(cls, input_val):
99 101 return float(input_val)
100 102
101 103 @classmethod
102 104 def _dir_func(cls, input_val, ensure_dir=False, mode=0o755):
103 105
104 106 # ensure we have our dir created
105 107 if not os.path.isdir(input_val) and ensure_dir:
106 108 os.makedirs(input_val, mode=mode)
107 109
108 110 if not os.path.isdir(input_val):
109 111 raise Exception('Dir at {} does not exist'.format(input_val))
110 112 return input_val
111 113
112 114 @classmethod
113 115 def _file_path_func(cls, input_val, ensure_dir=False, mode=0o755):
114 116 dirname = os.path.dirname(input_val)
115 117 cls._dir_func(dirname, ensure_dir=ensure_dir)
116 118 return input_val
117 119
118 120 @classmethod
119 121 def _key_transformator(cls, key):
120 122 return "{}_{}".format('RC'.upper(), key.upper().replace('.', '_').replace('-', '_'))
121 123
124 def maybe_env_key(self, key):
125 # now maybe we have this KEY in env, search and use the value with higher priority.
126 transformed_key = self._key_transformator(key)
127 envvar_value = os.environ.get(transformed_key)
128 if envvar_value:
129 log.debug('using `%s` key instead of `%s` key for config', transformed_key, key)
130
131 return envvar_value
132
133 def env_expand(self):
134 replaced = {}
135 for k, v in self.settings.items():
136 if k not in set_keys:
137 envvar_value = self.maybe_env_key(k)
138 if envvar_value:
139 replaced[k] = envvar_value
140 set_keys[k] = envvar_value
141
142 # replace ALL keys updated
143 self.settings.update(replaced)
144
122 145 def enable_logging(self, logging_conf=None, level='INFO', formatter='generic'):
123 146 """
124 147 Helper to enable debug on running instance
125 148 :return:
126 149 """
127 150
128 151 if not str2bool(self.settings.get('logging.autoconfigure')):
129 152 log.info('logging configuration based on main .ini file')
130 153 return
131 154
132 155 if logging_conf is None:
133 156 logging_conf = self.settings.get('logging.logging_conf_file') or ''
134 157
135 158 if not os.path.isfile(logging_conf):
136 log.error('Unable to setup logging based on %s, file does not exist...', logging_conf)
159 log.error('Unable to setup logging based on %s, '
160 'file does not exist.... specify path using logging.logging_conf_file= config setting. ', logging_conf)
137 161 return
138 162
139 163 with open(logging_conf, 'rb') as f:
140 164 ini_template = textwrap.dedent(f.read())
141 165 ini_template = string.Template(ini_template).safe_substitute(
142 166 RC_LOGGING_LEVEL=os.environ.get('RC_LOGGING_LEVEL', '') or level,
143 167 RC_LOGGING_FORMATTER=os.environ.get('RC_LOGGING_FORMATTER', '') or formatter
144 168 )
145 169
146 170 with tempfile.NamedTemporaryFile(prefix='rc_logging_', suffix='.ini', delete=False) as f:
147 171 log.info('Saved Temporary LOGGING config at %s', f.name)
148 172 f.write(ini_template)
149 173
150 174 logging.config.fileConfig(f.name)
151 175 os.remove(f.name)
152 176
153 177 def make_setting(self, key, default, lower=False, default_when_empty=False, parser=None):
154
155 178 input_val = self.settings.get(key, default)
156 179
157 180 if default_when_empty and not input_val:
158 181 # use default value when value is set in the config but it is empty
159 182 input_val = default
160 183
161 184 parser_func = {
162 185 'bool': self._bool_func,
163 186 'int': self._int_func,
164 187 'list': self._list_func,
165 188 'list:newline': functools.partial(self._list_func, sep='/n'),
166 189 'list:spacesep': functools.partial(self._list_func, sep=' '),
167 190 'string': functools.partial(self._string_func, lower=lower),
168 191 'dir': self._dir_func,
169 192 'dir:ensured': functools.partial(self._dir_func, ensure_dir=True),
170 193 'file': self._file_path_func,
171 194 'file:ensured': functools.partial(self._file_path_func, ensure_dir=True),
172 195 None: lambda i: i
173 196 }[parser]
174 197
175 # now maybe we have this KEY in env, search and use the value with higher priority.
176 transformed_key = self._key_transformator(key)
177 envvar_value = os.environ.get(transformed_key)
198 envvar_value = self.maybe_env_key(key)
178 199 if envvar_value:
179 log.debug('using `%s` key instead of `%s` key for config', transformed_key, key)
180 200 input_val = envvar_value
201 set_keys[key] = input_val
202
181 203 self.settings[key] = parser_func(input_val)
182 204 return self.settings[key]
General Comments 0
You need to be logged in to leave comments. Login now