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