##// END OF EJS Templates
exceptions: fixed .message deprecated in python3
super-admin -
r1112:cf373e01 python3
parent child Browse files
Show More
@@ -1,773 +1,773 b''
1 1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 2 # Copyright (C) 2014-2020 RhodeCode GmbH
3 3 #
4 4 # This program is free software; you can redistribute it and/or modify
5 5 # it under the terms of the GNU General Public License as published by
6 6 # the Free Software Foundation; either version 3 of the License, or
7 7 # (at your option) any later version.
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 General Public License
15 15 # along with this program; if not, write to the Free Software Foundation,
16 16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 17
18 18 import io
19 19 import os
20 20 import sys
21 21 import base64
22 22 import locale
23 23 import logging
24 24 import uuid
25 25 import time
26 26 import wsgiref.util
27 27 import traceback
28 28 import tempfile
29 29 import psutil
30 30
31 31 from itertools import chain
32 32
33 33 import msgpack
34 34 import configparser
35 35
36 36 from pyramid.config import Configurator
37 37 from pyramid.wsgi import wsgiapp
38 38 from pyramid.response import Response
39 39
40 40 from vcsserver.base import BinaryEnvelope
41 41 from vcsserver.lib.rc_json import json
42 42 from vcsserver.config.settings_maker import SettingsMaker
43 43 from vcsserver.str_utils import safe_int, safe_bytes, safe_str
44 44 from vcsserver.lib.statsd_client import StatsdClient
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48 # due to Mercurial/glibc2.27 problems we need to detect if locale settings are
49 49 # causing problems and "fix" it in case they do and fallback to LC_ALL = C
50 50
51 51 try:
52 52 locale.setlocale(locale.LC_ALL, '')
53 53 except locale.Error as e:
54 54 log.error(
55 55 'LOCALE ERROR: failed to set LC_ALL, fallback to LC_ALL=C, org error: %s', e)
56 56 os.environ['LC_ALL'] = 'C'
57 57
58 58
59 59 import vcsserver
60 60 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
61 61 from vcsserver.git_lfs.app import GIT_LFS_CONTENT_TYPE, GIT_LFS_PROTO_PAT
62 62 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
63 63 from vcsserver.echo_stub.echo_app import EchoApp
64 64 from vcsserver.exceptions import HTTPRepoLocked, HTTPRepoBranchProtected
65 65 from vcsserver.lib.exc_tracking import store_exception
66 66 from vcsserver.server import VcsServer
67 67
68 68 strict_vcs = True
69 69
70 70 git_import_err = None
71 71 try:
72 72 from vcsserver.remote.git import GitFactory, GitRemote
73 73 except ImportError as e:
74 74 GitFactory = None
75 75 GitRemote = None
76 76 git_import_err = e
77 77 if strict_vcs:
78 78 raise
79 79
80 80
81 81 hg_import_err = None
82 82 try:
83 83 from vcsserver.remote.hg import MercurialFactory, HgRemote
84 84 except ImportError as e:
85 85 MercurialFactory = None
86 86 HgRemote = None
87 87 hg_import_err = e
88 88 if strict_vcs:
89 89 raise
90 90
91 91
92 92 svn_import_err = None
93 93 try:
94 94 from vcsserver.remote.svn import SubversionFactory, SvnRemote
95 95 except ImportError as e:
96 96 SubversionFactory = None
97 97 SvnRemote = None
98 98 svn_import_err = e
99 99 if strict_vcs:
100 100 raise
101 101
102 102
103 103 def _is_request_chunked(environ):
104 104 stream = environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked'
105 105 return stream
106 106
107 107
108 108 def log_max_fd():
109 109 try:
110 110 maxfd = psutil.Process().rlimit(psutil.RLIMIT_NOFILE)[1]
111 111 log.info('Max file descriptors value: %s', maxfd)
112 112 except Exception:
113 113 pass
114 114
115 115
116 116 class VCS(object):
117 117 def __init__(self, locale_conf=None, cache_config=None):
118 118 self.locale = locale_conf
119 119 self.cache_config = cache_config
120 120 self._configure_locale()
121 121
122 122 log_max_fd()
123 123
124 124 if GitFactory and GitRemote:
125 125 git_factory = GitFactory()
126 126 self._git_remote = GitRemote(git_factory)
127 127 else:
128 128 log.error("Git client import failed: %s", git_import_err)
129 129
130 130 if MercurialFactory and HgRemote:
131 131 hg_factory = MercurialFactory()
132 132 self._hg_remote = HgRemote(hg_factory)
133 133 else:
134 134 log.error("Mercurial client import failed: %s", hg_import_err)
135 135
136 136 if SubversionFactory and SvnRemote:
137 137 svn_factory = SubversionFactory()
138 138
139 139 # hg factory is used for svn url validation
140 140 hg_factory = MercurialFactory()
141 141 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
142 142 else:
143 143 log.error("Subversion client import failed: %s", svn_import_err)
144 144
145 145 self._vcsserver = VcsServer()
146 146
147 147 def _configure_locale(self):
148 148 if self.locale:
149 149 log.info('Settings locale: `LC_ALL` to %s', self.locale)
150 150 else:
151 151 log.info('Configuring locale subsystem based on environment variables')
152 152 try:
153 153 # If self.locale is the empty string, then the locale
154 154 # module will use the environment variables. See the
155 155 # documentation of the package `locale`.
156 156 locale.setlocale(locale.LC_ALL, self.locale)
157 157
158 158 language_code, encoding = locale.getlocale()
159 159 log.info(
160 160 'Locale set to language code "%s" with encoding "%s".',
161 161 language_code, encoding)
162 162 except locale.Error:
163 163 log.exception('Cannot set locale, not configuring the locale system')
164 164
165 165
166 166 class WsgiProxy(object):
167 167 def __init__(self, wsgi):
168 168 self.wsgi = wsgi
169 169
170 170 def __call__(self, environ, start_response):
171 171 input_data = environ['wsgi.input'].read()
172 172 input_data = msgpack.unpackb(input_data)
173 173
174 174 error = None
175 175 try:
176 176 data, status, headers = self.wsgi.handle(
177 177 input_data['environment'], input_data['input_data'],
178 178 *input_data['args'], **input_data['kwargs'])
179 179 except Exception as e:
180 180 data, status, headers = [], None, None
181 181 error = {
182 182 'message': str(e),
183 183 '_vcs_kind': getattr(e, '_vcs_kind', None)
184 184 }
185 185
186 186 start_response(200, {})
187 187 return self._iterator(error, status, headers, data)
188 188
189 189 def _iterator(self, error, status, headers, data):
190 190 initial_data = [
191 191 error,
192 192 status,
193 193 headers,
194 194 ]
195 195
196 196 for d in chain(initial_data, data):
197 197 yield msgpack.packb(d)
198 198
199 199
200 200 def not_found(request):
201 201 return {'status': '404 NOT FOUND'}
202 202
203 203
204 204 class VCSViewPredicate(object):
205 205 def __init__(self, val, config):
206 206 self.remotes = val
207 207
208 208 def text(self):
209 209 return 'vcs view method = %s' % (list(self.remotes.keys()),)
210 210
211 211 phash = text
212 212
213 213 def __call__(self, context, request):
214 214 """
215 215 View predicate that returns true if given backend is supported by
216 216 defined remotes.
217 217 """
218 218 backend = request.matchdict.get('backend')
219 219 return backend in self.remotes
220 220
221 221
222 222 class HTTPApplication(object):
223 223 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
224 224
225 225 remote_wsgi = remote_wsgi
226 226 _use_echo_app = False
227 227
228 228 def __init__(self, settings=None, global_config=None):
229 229
230 230 self.config = Configurator(settings=settings)
231 231 # Init our statsd at very start
232 232 self.config.registry.statsd = StatsdClient.statsd
233 233 self.config.registry.vcs_call_context = {}
234 234
235 235 self.global_config = global_config
236 236 self.config.include('vcsserver.lib.rc_cache')
237 237
238 238 settings_locale = settings.get('locale', '') or 'en_US.UTF-8'
239 239 vcs = VCS(locale_conf=settings_locale, cache_config=settings)
240 240 self._remotes = {
241 241 'hg': vcs._hg_remote,
242 242 'git': vcs._git_remote,
243 243 'svn': vcs._svn_remote,
244 244 'server': vcs._vcsserver,
245 245 }
246 246 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
247 247 self._use_echo_app = True
248 248 log.warning("Using EchoApp for VCS operations.")
249 249 self.remote_wsgi = remote_wsgi_stub
250 250
251 251 self._configure_settings(global_config, settings)
252 252
253 253 self._configure()
254 254
255 255 def _configure_settings(self, global_config, app_settings):
256 256 """
257 257 Configure the settings module.
258 258 """
259 259 settings_merged = global_config.copy()
260 260 settings_merged.update(app_settings)
261 261
262 262 git_path = app_settings.get('git_path', None)
263 263 if git_path:
264 264 settings.GIT_EXECUTABLE = git_path
265 265 binary_dir = app_settings.get('core.binary_dir', None)
266 266 if binary_dir:
267 267 settings.BINARY_DIR = binary_dir
268 268
269 269 # Store the settings to make them available to other modules.
270 270 vcsserver.PYRAMID_SETTINGS = settings_merged
271 271 vcsserver.CONFIG = settings_merged
272 272
273 273 def _configure(self):
274 274 self.config.add_renderer(name='msgpack', factory=self._msgpack_renderer_factory)
275 275
276 276 self.config.add_route('service', '/_service')
277 277 self.config.add_route('status', '/status')
278 278 self.config.add_route('hg_proxy', '/proxy/hg')
279 279 self.config.add_route('git_proxy', '/proxy/git')
280 280
281 281 # rpc methods
282 282 self.config.add_route('vcs', '/{backend}')
283 283
284 284 # streaming rpc remote methods
285 285 self.config.add_route('vcs_stream', '/{backend}/stream')
286 286
287 287 # vcs operations clone/push as streaming
288 288 self.config.add_route('stream_git', '/stream/git/*repo_name')
289 289 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
290 290
291 291 self.config.add_view(self.status_view, route_name='status', renderer='json')
292 292 self.config.add_view(self.service_view, route_name='service', renderer='msgpack')
293 293
294 294 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
295 295 self.config.add_view(self.git_proxy(), route_name='git_proxy')
296 296 self.config.add_view(self.vcs_view, route_name='vcs', renderer='msgpack',
297 297 vcs_view=self._remotes)
298 298 self.config.add_view(self.vcs_stream_view, route_name='vcs_stream',
299 299 vcs_view=self._remotes)
300 300
301 301 self.config.add_view(self.hg_stream(), route_name='stream_hg')
302 302 self.config.add_view(self.git_stream(), route_name='stream_git')
303 303
304 304 self.config.add_view_predicate('vcs_view', VCSViewPredicate)
305 305
306 306 self.config.add_notfound_view(not_found, renderer='json')
307 307
308 308 self.config.add_view(self.handle_vcs_exception, context=Exception)
309 309
310 310 self.config.add_tween(
311 311 'vcsserver.tweens.request_wrapper.RequestWrapperTween',
312 312 )
313 313 self.config.add_request_method(
314 314 'vcsserver.lib.request_counter.get_request_counter',
315 315 'request_count')
316 316
317 317 def wsgi_app(self):
318 318 return self.config.make_wsgi_app()
319 319
320 320 def _vcs_view_params(self, request):
321 321 remote = self._remotes[request.matchdict['backend']]
322 322 payload = msgpack.unpackb(request.body, use_list=True)
323 323
324 324 method = payload.get('method')
325 325 params = payload['params']
326 326 wire = params.get('wire')
327 327 args = params.get('args')
328 328 kwargs = params.get('kwargs')
329 329 context_uid = None
330 330
331 331 request.registry.vcs_call_context = {
332 332 'method': method,
333 333 'repo_name': payload.get('_repo_name')
334 334 }
335 335
336 336 if wire:
337 337 try:
338 338 wire['context'] = context_uid = uuid.UUID(wire['context'])
339 339 except KeyError:
340 340 pass
341 341 args.insert(0, wire)
342 342 repo_state_uid = wire.get('repo_state_uid') if wire else None
343 343
344 344 # NOTE(marcink): trading complexity for slight performance
345 345 if log.isEnabledFor(logging.DEBUG):
346 346 # also we SKIP printing out any of those methods args since they maybe excessive
347 347 just_args_methods = {
348 348 'commitctx': ('content', 'removed', 'updated')
349 349 }
350 350 if method in just_args_methods:
351 351 skip_args = just_args_methods[method]
352 352 call_args = ''
353 353 call_kwargs = {}
354 354 for k in kwargs:
355 355 if k in skip_args:
356 356 # replace our skip key with dummy
357 357 call_kwargs[k] = f'RemovedParam({k})'
358 358 else:
359 359 call_kwargs[k] = kwargs[k]
360 360 else:
361 361 call_args = args[1:]
362 362 call_kwargs = kwargs
363 363
364 364 log.debug('Method requested:`%s` with args:%s kwargs:%s context_uid: %s, repo_state_uid:%s',
365 365 method, call_args, call_kwargs, context_uid, repo_state_uid)
366 366
367 367 statsd = request.registry.statsd
368 368 if statsd:
369 369 statsd.incr(
370 370 'vcsserver_method_total', tags=[
371 371 "method:{}".format(method),
372 372 ])
373 373 return payload, remote, method, args, kwargs
374 374
375 375 def vcs_view(self, request):
376 376
377 377 payload, remote, method, args, kwargs = self._vcs_view_params(request)
378 378 payload_id = payload.get('id')
379 379
380 380 try:
381 381 resp = getattr(remote, method)(*args, **kwargs)
382 382 except Exception as e:
383 383 exc_info = list(sys.exc_info())
384 384 exc_type, exc_value, exc_traceback = exc_info
385 385
386 386 org_exc = getattr(e, '_org_exc', None)
387 387 org_exc_name = None
388 388 org_exc_tb = ''
389 389 if org_exc:
390 390 org_exc_name = org_exc.__class__.__name__
391 391 org_exc_tb = getattr(e, '_org_exc_tb', '')
392 392 # replace our "faked" exception with our org
393 393 exc_info[0] = org_exc.__class__
394 394 exc_info[1] = org_exc
395 395
396 396 should_store_exc = True
397 397 if org_exc:
398 398 def get_exc_fqn(_exc_obj):
399 399 module_name = getattr(org_exc.__class__, '__module__', 'UNKNOWN')
400 400 return module_name + '.' + org_exc_name
401 401
402 402 exc_fqn = get_exc_fqn(org_exc)
403 403
404 404 if exc_fqn in ['mercurial.error.RepoLookupError',
405 405 'vcsserver.exceptions.RefNotFoundException']:
406 406 should_store_exc = False
407 407
408 408 if should_store_exc:
409 409 store_exception(id(exc_info), exc_info, request_path=request.path)
410 410
411 411 tb_info = ''.join(
412 412 traceback.format_exception(exc_type, exc_value, exc_traceback))
413 413
414 414 type_ = e.__class__.__name__
415 415 if type_ not in self.ALLOWED_EXCEPTIONS:
416 416 type_ = None
417 417
418 418 resp = {
419 419 'id': payload_id,
420 420 'error': {
421 421 'message': str(e),
422 422 'traceback': tb_info,
423 423 'org_exc': org_exc_name,
424 424 'org_exc_tb': org_exc_tb,
425 425 'type': type_
426 426 }
427 427 }
428 428
429 429 try:
430 430 resp['error']['_vcs_kind'] = getattr(e, '_vcs_kind', None)
431 431 except AttributeError:
432 432 pass
433 433 else:
434 434 resp = {
435 435 'id': payload_id,
436 436 'result': resp
437 437 }
438 438 log.debug('Serving data for method %s', method)
439 439 return resp
440 440
441 441 def vcs_stream_view(self, request):
442 442 payload, remote, method, args, kwargs = self._vcs_view_params(request)
443 443 # this method has a stream: marker we remove it here
444 444 method = method.split('stream:')[-1]
445 445 chunk_size = safe_int(payload.get('chunk_size')) or 4096
446 446
447 447 try:
448 448 resp = getattr(remote, method)(*args, **kwargs)
449 449 except Exception as e:
450 450 raise
451 451
452 452 def get_chunked_data(method_resp):
453 453 stream = io.BytesIO(method_resp)
454 454 while 1:
455 455 chunk = stream.read(chunk_size)
456 456 if not chunk:
457 457 break
458 458 yield chunk
459 459
460 460 response = Response(app_iter=get_chunked_data(resp))
461 461 response.content_type = 'application/octet-stream'
462 462
463 463 return response
464 464
465 465 def status_view(self, request):
466 466 import vcsserver
467 467 return {'status': 'OK', 'vcsserver_version': vcsserver.__version__,
468 468 'pid': os.getpid()}
469 469
470 470 def service_view(self, request):
471 471 import vcsserver
472 472
473 473 payload = msgpack.unpackb(request.body, use_list=True)
474 474 server_config, app_config = {}, {}
475 475
476 476 try:
477 477 path = self.global_config['__file__']
478 478 config = configparser.RawConfigParser()
479 479
480 480 config.read(path)
481 481
482 482 if config.has_section('server:main'):
483 483 server_config = dict(config.items('server:main'))
484 484 if config.has_section('app:main'):
485 485 app_config = dict(config.items('app:main'))
486 486
487 487 except Exception:
488 488 log.exception('Failed to read .ini file for display')
489 489
490 490 environ = list(os.environ.items())
491 491
492 492 resp = {
493 493 'id': payload.get('id'),
494 494 'result': dict(
495 495 version=vcsserver.__version__,
496 496 config=server_config,
497 497 app_config=app_config,
498 498 environ=environ,
499 499 payload=payload,
500 500 )
501 501 }
502 502 return resp
503 503
504 504 def _msgpack_renderer_factory(self, info):
505 505
506 506 def _render(value, system):
507 507 bin_type = False
508 508 res = value.get('result')
509 509 if res and isinstance(res, BinaryEnvelope):
510 510 log.debug('Result is wrapped in BinaryEnvelope type')
511 511 value['result'] = res.value
512 512 bin_type = res.bin_type
513 513
514 514 request = system.get('request')
515 515 if request is not None:
516 516 response = request.response
517 517 ct = response.content_type
518 518 if ct == response.default_content_type:
519 519 response.content_type = 'application/x-msgpack'
520 520 if bin_type:
521 521 response.content_type = 'application/x-msgpack-bin'
522 522
523 523 return msgpack.packb(value, use_bin_type=bin_type)
524 524 return _render
525 525
526 526 def set_env_from_config(self, environ, config):
527 527 dict_conf = {}
528 528 try:
529 529 for elem in config:
530 530 if elem[0] == 'rhodecode':
531 531 dict_conf = json.loads(elem[2])
532 532 break
533 533 except Exception:
534 534 log.exception('Failed to fetch SCM CONFIG')
535 535 return
536 536
537 537 username = dict_conf.get('username')
538 538 if username:
539 539 environ['REMOTE_USER'] = username
540 540 # mercurial specific, some extension api rely on this
541 541 environ['HGUSER'] = username
542 542
543 543 ip = dict_conf.get('ip')
544 544 if ip:
545 545 environ['REMOTE_HOST'] = ip
546 546
547 547 if _is_request_chunked(environ):
548 548 # set the compatibility flag for webob
549 549 environ['wsgi.input_terminated'] = True
550 550
551 551 def hg_proxy(self):
552 552 @wsgiapp
553 553 def _hg_proxy(environ, start_response):
554 554 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
555 555 return app(environ, start_response)
556 556 return _hg_proxy
557 557
558 558 def git_proxy(self):
559 559 @wsgiapp
560 560 def _git_proxy(environ, start_response):
561 561 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
562 562 return app(environ, start_response)
563 563 return _git_proxy
564 564
565 565 def hg_stream(self):
566 566 if self._use_echo_app:
567 567 @wsgiapp
568 568 def _hg_stream(environ, start_response):
569 569 app = EchoApp('fake_path', 'fake_name', None)
570 570 return app(environ, start_response)
571 571 return _hg_stream
572 572 else:
573 573 @wsgiapp
574 574 def _hg_stream(environ, start_response):
575 575 log.debug('http-app: handling hg stream')
576 576
577 577 packed_cc = base64.b64decode(environ['HTTP_X_RC_VCS_STREAM_CALL_CONTEXT'])
578 578 call_context = msgpack.unpackb(packed_cc)
579 579
580 580 repo_path = call_context['repo_path']
581 581 repo_name = call_context['repo_name']
582 582 config = call_context['repo_config']
583 583
584 584 app = scm_app.create_hg_wsgi_app(
585 585 repo_path, repo_name, config)
586 586
587 587 # Consistent path information for hgweb
588 588 environ['PATH_INFO'] = call_context['path_info']
589 589 environ['REPO_NAME'] = repo_name
590 590 self.set_env_from_config(environ, config)
591 591
592 592 log.debug('http-app: starting app handler '
593 593 'with %s and process request', app)
594 594 return app(environ, ResponseFilter(start_response))
595 595 return _hg_stream
596 596
597 597 def git_stream(self):
598 598 if self._use_echo_app:
599 599 @wsgiapp
600 600 def _git_stream(environ, start_response):
601 601 app = EchoApp('fake_path', 'fake_name', None)
602 602 return app(environ, start_response)
603 603 return _git_stream
604 604 else:
605 605 @wsgiapp
606 606 def _git_stream(environ, start_response):
607 607 log.debug('http-app: handling git stream')
608 608
609 609 packed_cc = base64.b64decode(environ['HTTP_X_RC_VCS_STREAM_CALL_CONTEXT'])
610 610 call_context = msgpack.unpackb(packed_cc)
611 611
612 612 repo_path = call_context['repo_path']
613 613 repo_name = call_context['repo_name']
614 614 config = call_context['repo_config']
615 615
616 616 environ['PATH_INFO'] = call_context['path_info']
617 617 self.set_env_from_config(environ, config)
618 618
619 619 content_type = environ.get('CONTENT_TYPE', '')
620 620
621 621 path = environ['PATH_INFO']
622 622 is_lfs_request = GIT_LFS_CONTENT_TYPE in content_type
623 623 log.debug(
624 624 'LFS: Detecting if request `%s` is LFS server path based '
625 625 'on content type:`%s`, is_lfs:%s',
626 626 path, content_type, is_lfs_request)
627 627
628 628 if not is_lfs_request:
629 629 # fallback detection by path
630 630 if GIT_LFS_PROTO_PAT.match(path):
631 631 is_lfs_request = True
632 632 log.debug(
633 633 'LFS: fallback detection by path of: `%s`, is_lfs:%s',
634 634 path, is_lfs_request)
635 635
636 636 if is_lfs_request:
637 637 app = scm_app.create_git_lfs_wsgi_app(
638 638 repo_path, repo_name, config)
639 639 else:
640 640 app = scm_app.create_git_wsgi_app(
641 641 repo_path, repo_name, config)
642 642
643 643 log.debug('http-app: starting app handler '
644 644 'with %s and process request', app)
645 645
646 646 return app(environ, start_response)
647 647
648 648 return _git_stream
649 649
650 650 def handle_vcs_exception(self, exception, request):
651 651 _vcs_kind = getattr(exception, '_vcs_kind', '')
652 652 if _vcs_kind == 'repo_locked':
653 653 # Get custom repo-locked status code if present.
654 654 status_code = request.headers.get('X-RC-Locked-Status-Code')
655 655 return HTTPRepoLocked(
656 title=exception.message, status_code=status_code)
656 title=str(exception), status_code=status_code)
657 657
658 658 elif _vcs_kind == 'repo_branch_protected':
659 659 # Get custom repo-branch-protected status code if present.
660 return HTTPRepoBranchProtected(title=exception.message)
660 return HTTPRepoBranchProtected(title=str(exception))
661 661
662 662 exc_info = request.exc_info
663 663 store_exception(id(exc_info), exc_info)
664 664
665 665 traceback_info = 'unavailable'
666 666 if request.exc_info:
667 667 exc_type, exc_value, exc_tb = request.exc_info
668 668 traceback_info = ''.join(traceback.format_exception(exc_type, exc_value, exc_tb))
669 669
670 670 log.error(
671 671 'error occurred handling this request for path: %s, \n tb: %s',
672 672 request.path, traceback_info)
673 673
674 674 statsd = request.registry.statsd
675 675 if statsd:
676 676 exc_type = "{}.{}".format(exception.__class__.__module__, exception.__class__.__name__)
677 677 statsd.incr('vcsserver_exception_total',
678 678 tags=["type:{}".format(exc_type)])
679 679 raise exception
680 680
681 681
682 682 class ResponseFilter(object):
683 683
684 684 def __init__(self, start_response):
685 685 self._start_response = start_response
686 686
687 687 def __call__(self, status, response_headers, exc_info=None):
688 688 headers = tuple(
689 689 (h, v) for h, v in response_headers
690 690 if not wsgiref.util.is_hop_by_hop(h))
691 691 return self._start_response(status, headers, exc_info)
692 692
693 693
694 694 def sanitize_settings_and_apply_defaults(global_config, settings):
695 695 global_settings_maker = SettingsMaker(global_config)
696 696 settings_maker = SettingsMaker(settings)
697 697
698 698 settings_maker.make_setting('logging.autoconfigure', False, parser='bool')
699 699
700 700 logging_conf = os.path.join(os.path.dirname(global_config.get('__file__')), 'logging.ini')
701 701 settings_maker.enable_logging(logging_conf)
702 702
703 703 # Default includes, possible to change as a user
704 704 pyramid_includes = settings_maker.make_setting('pyramid.includes', [], parser='list:newline')
705 705 log.debug("Using the following pyramid.includes: %s", pyramid_includes)
706 706
707 707 settings_maker.make_setting('__file__', global_config.get('__file__'))
708 708
709 709 settings_maker.make_setting('pyramid.default_locale_name', 'en')
710 710 settings_maker.make_setting('locale', 'en_US.UTF-8')
711 711
712 712 settings_maker.make_setting('core.binary_dir', '')
713 713
714 714 temp_store = tempfile.gettempdir()
715 715 default_cache_dir = os.path.join(temp_store, 'rc_cache')
716 716 # save default, cache dir, and use it for all backends later.
717 717 default_cache_dir = settings_maker.make_setting(
718 718 'cache_dir',
719 719 default=default_cache_dir, default_when_empty=True,
720 720 parser='dir:ensured')
721 721
722 722 # exception store cache
723 723 settings_maker.make_setting(
724 724 'exception_tracker.store_path',
725 725 default=os.path.join(default_cache_dir, 'exc_store'), default_when_empty=True,
726 726 parser='dir:ensured'
727 727 )
728 728
729 729 # repo_object cache defaults
730 730 settings_maker.make_setting(
731 731 'rc_cache.repo_object.backend',
732 732 default='dogpile.cache.rc.file_namespace',
733 733 parser='string')
734 734 settings_maker.make_setting(
735 735 'rc_cache.repo_object.expiration_time',
736 736 default=30 * 24 * 60 * 60, # 30days
737 737 parser='int')
738 738 settings_maker.make_setting(
739 739 'rc_cache.repo_object.arguments.filename',
740 740 default=os.path.join(default_cache_dir, 'vcsserver_cache_repo_object.db'),
741 741 parser='string')
742 742
743 743 # statsd
744 744 settings_maker.make_setting('statsd.enabled', False, parser='bool')
745 745 settings_maker.make_setting('statsd.statsd_host', 'statsd-exporter', parser='string')
746 746 settings_maker.make_setting('statsd.statsd_port', 9125, parser='int')
747 747 settings_maker.make_setting('statsd.statsd_prefix', '')
748 748 settings_maker.make_setting('statsd.statsd_ipv6', False, parser='bool')
749 749
750 750 settings_maker.env_expand()
751 751
752 752
753 753 def main(global_config, **settings):
754 754 start_time = time.time()
755 755 log.info('Pyramid app config starting')
756 756
757 757 if MercurialFactory:
758 758 hgpatches.patch_largefiles_capabilities()
759 759 hgpatches.patch_subrepo_type_mapping()
760 760
761 761 # Fill in and sanitize the defaults & do ENV expansion
762 762 sanitize_settings_and_apply_defaults(global_config, settings)
763 763
764 764 # init and bootstrap StatsdClient
765 765 StatsdClient.setup(settings)
766 766
767 767 pyramid_app = HTTPApplication(settings=settings, global_config=global_config).wsgi_app()
768 768 total_time = time.time() - start_time
769 769 log.info('Pyramid app `%s` created and configured in %.2fs',
770 770 getattr(pyramid_app, 'func_name', 'pyramid_app'), total_time)
771 771 return pyramid_app
772 772
773 773
General Comments 0
You need to be logged in to leave comments. Login now