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