##// END OF EJS Templates
fix(settings): fixed a case for certain settings that could be quoted inside .ini files
super-admin -
r1215:685ad2d3 default
parent child Browse files
Show More
@@ -1,204 +1,204 b''
1 1 #
2 2
3 3 ; #################################
4 4 ; RHODECODE VCSSERVER CONFIGURATION
5 5 ; #################################
6 6
7 7 [server:main]
8 8 ; COMMON HOST/IP CONFIG
9 9 host = 0.0.0.0
10 10 port = 10010
11 11
12 12 ; ##################################################
13 13 ; WAITRESS WSGI SERVER - Recommended for Development
14 14 ; ##################################################
15 15
16 16 ; use server type
17 17 use = egg:waitress#main
18 18
19 19 ; number of worker threads
20 20 threads = 5
21 21
22 22 ; MAX BODY SIZE 100GB
23 23 max_request_body_size = 107374182400
24 24
25 25 ; Use poll instead of select, fixes file descriptors limits problems.
26 26 ; May not work on old windows systems.
27 27 asyncore_use_poll = true
28 28
29 29
30 30 ; ###########################
31 31 ; GUNICORN APPLICATION SERVER
32 32 ; ###########################
33 33
34 34 ; run with gunicorn --paste rhodecode.ini
35 35
36 36 ; Module to use, this setting shouldn't be changed
37 37 #use = egg:gunicorn#main
38 38
39 39 [app:main]
40 40 ; The %(here)s variable will be replaced with the absolute path of parent directory
41 41 ; of this file
42 42 ; Each option in the app:main can be override by an environmental variable
43 43 ;
44 44 ;To override an option:
45 45 ;
46 46 ;RC_<KeyName>
47 47 ;Everything should be uppercase, . and - should be replaced by _.
48 48 ;For example, if you have these configuration settings:
49 49 ;rc_cache.repo_object.backend = foo
50 50 ;can be overridden by
51 51 ;export RC_CACHE_REPO_OBJECT_BACKEND=foo
52 52
53 53 use = egg:rhodecode-vcsserver
54 54
55 55
56 56 ; #############
57 57 ; DEBUG OPTIONS
58 58 ; #############
59 59
60 60 # During development the we want to have the debug toolbar enabled
61 61 pyramid.includes =
62 62 pyramid_debugtoolbar
63 63
64 64 debugtoolbar.hosts = 0.0.0.0/0
65 65 debugtoolbar.exclude_prefixes =
66 66 /css
67 67 /fonts
68 68 /images
69 69 /js
70 70
71 71 ; #################
72 72 ; END DEBUG OPTIONS
73 73 ; #################
74 74
75 75 ; Pyramid default locales, we need this to be set
76 76 #pyramid.default_locale_name = en
77 77
78 78 ; default locale used by VCS systems
79 79 #locale = en_US.UTF-8
80 80
81 ; path to binaries for vcsserver, it should be set by the installer
81 ; path to binaries (hg,git,svn) for vcsserver, it should be set by the installer
82 82 ; at installation time, e.g /home/user/.rccontrol/vcsserver-1/profile/bin
83 ; it can also be a path to nix-build output in case of development
84 core.binary_dir = ""
83 ; or /usr/local/bin/rhodecode_bin/vcs_bin
84 core.binary_dir =
85 85
86 86 ; Custom exception store path, defaults to TMPDIR
87 87 ; This is used to store exception from RhodeCode in shared directory
88 88 #exception_tracker.store_path =
89 89
90 90 ; #############
91 91 ; DOGPILE CACHE
92 92 ; #############
93 93
94 94 ; Default cache dir for caches. Putting this into a ramdisk can boost performance.
95 95 ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space
96 96 #cache_dir = %(here)s/data
97 97
98 98 ; ***************************************
99 99 ; `repo_object` cache, default file based
100 100 ; ***************************************
101 101
102 102 ; `repo_object` cache settings for vcs methods for repositories
103 103 #rc_cache.repo_object.backend = dogpile.cache.rc.file_namespace
104 104
105 105 ; cache auto-expires after N seconds
106 106 ; Examples: 86400 (1Day), 604800 (7Days), 1209600 (14Days), 2592000 (30days), 7776000 (90Days)
107 107 #rc_cache.repo_object.expiration_time = 2592000
108 108
109 109 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
110 110 #rc_cache.repo_object.arguments.filename = /tmp/vcsserver_cache_repo_object.db
111 111
112 112 ; ***********************************************************
113 113 ; `repo_object` cache with redis backend
114 114 ; recommended for larger instance, and for better performance
115 115 ; ***********************************************************
116 116
117 117 ; `repo_object` cache settings for vcs methods for repositories
118 118 #rc_cache.repo_object.backend = dogpile.cache.rc.redis_msgpack
119 119
120 120 ; cache auto-expires after N seconds
121 121 ; Examples: 86400 (1Day), 604800 (7Days), 1209600 (14Days), 2592000 (30days), 7776000 (90Days)
122 122 #rc_cache.repo_object.expiration_time = 2592000
123 123
124 124 ; redis_expiration_time needs to be greater then expiration_time
125 125 #rc_cache.repo_object.arguments.redis_expiration_time = 3592000
126 126
127 127 #rc_cache.repo_object.arguments.host = localhost
128 128 #rc_cache.repo_object.arguments.port = 6379
129 129 #rc_cache.repo_object.arguments.db = 5
130 130 #rc_cache.repo_object.arguments.socket_timeout = 30
131 131 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
132 132 #rc_cache.repo_object.arguments.distributed_lock = true
133 133
134 134 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
135 135 #rc_cache.repo_object.arguments.lock_auto_renewal = true
136 136
137 137 ; Statsd client config, this is used to send metrics to statsd
138 138 ; We recommend setting statsd_exported and scrape them using Promethues
139 139 #statsd.enabled = false
140 140 #statsd.statsd_host = 0.0.0.0
141 141 #statsd.statsd_port = 8125
142 142 #statsd.statsd_prefix =
143 143 #statsd.statsd_ipv6 = false
144 144
145 145 ; configure logging automatically at server startup set to false
146 146 ; to use the below custom logging config.
147 147 ; RC_LOGGING_FORMATTER
148 148 ; RC_LOGGING_LEVEL
149 149 ; env variables can control the settings for logging in case of autoconfigure
150 150
151 151 #logging.autoconfigure = true
152 152
153 153 ; specify your own custom logging config file to configure logging
154 154 #logging.logging_conf_file = /path/to/custom_logging.ini
155 155
156 156 ; #####################
157 157 ; LOGGING CONFIGURATION
158 158 ; #####################
159 159
160 160 [loggers]
161 161 keys = root, vcsserver
162 162
163 163 [handlers]
164 164 keys = console
165 165
166 166 [formatters]
167 167 keys = generic, json
168 168
169 169 ; #######
170 170 ; LOGGERS
171 171 ; #######
172 172 [logger_root]
173 173 level = NOTSET
174 174 handlers = console
175 175
176 176 [logger_vcsserver]
177 177 level = DEBUG
178 178 handlers =
179 179 qualname = vcsserver
180 180 propagate = 1
181 181
182 182 ; ########
183 183 ; HANDLERS
184 184 ; ########
185 185
186 186 [handler_console]
187 187 class = StreamHandler
188 188 args = (sys.stderr, )
189 189 level = DEBUG
190 190 ; To enable JSON formatted logs replace 'generic' with 'json'
191 191 ; This allows sending properly formatted logs to grafana loki or elasticsearch
192 192 formatter = generic
193 193
194 194 ; ##########
195 195 ; FORMATTERS
196 196 ; ##########
197 197
198 198 [formatter_generic]
199 199 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
200 200 datefmt = %Y-%m-%d %H:%M:%S
201 201
202 202 [formatter_json]
203 203 format = %(timestamp)s %(levelname)s %(name)s %(message)s %(req_id)s
204 204 class = vcsserver.lib._vendor.jsonlogger.JsonFormatter
@@ -1,167 +1,167 b''
1 1 #
2 2
3 3 ; #################################
4 4 ; RHODECODE VCSSERVER CONFIGURATION
5 5 ; #################################
6 6
7 7 [server:main]
8 8 ; COMMON HOST/IP CONFIG
9 9 host = 127.0.0.1
10 10 port = 10010
11 11
12 12
13 13 ; ###########################
14 14 ; GUNICORN APPLICATION SERVER
15 15 ; ###########################
16 16
17 17 ; run with gunicorn --paste rhodecode.ini
18 18
19 19 ; Module to use, this setting shouldn't be changed
20 20 use = egg:gunicorn#main
21 21
22 22 [app:main]
23 23 ; The %(here)s variable will be replaced with the absolute path of parent directory
24 24 ; of this file
25 25 ; Each option in the app:main can be override by an environmental variable
26 26 ;
27 27 ;To override an option:
28 28 ;
29 29 ;RC_<KeyName>
30 30 ;Everything should be uppercase, . and - should be replaced by _.
31 31 ;For example, if you have these configuration settings:
32 32 ;rc_cache.repo_object.backend = foo
33 33 ;can be overridden by
34 34 ;export RC_CACHE_REPO_OBJECT_BACKEND=foo
35 35
36 36 use = egg:rhodecode-vcsserver
37 37
38 38 ; Pyramid default locales, we need this to be set
39 39 #pyramid.default_locale_name = en
40 40
41 41 ; default locale used by VCS systems
42 42 #locale = en_US.UTF-8
43 43
44 ; path to binaries for vcsserver, it should be set by the installer
44 ; path to binaries (hg,git,svn) for vcsserver, it should be set by the installer
45 45 ; at installation time, e.g /home/user/.rccontrol/vcsserver-1/profile/bin
46 ; it can also be a path to nix-build output in case of development
47 core.binary_dir = ""
46 ; or /usr/local/bin/rhodecode_bin/vcs_bin
47 core.binary_dir =
48 48
49 49 ; Custom exception store path, defaults to TMPDIR
50 50 ; This is used to store exception from RhodeCode in shared directory
51 51 #exception_tracker.store_path =
52 52
53 53 ; #############
54 54 ; DOGPILE CACHE
55 55 ; #############
56 56
57 57 ; Default cache dir for caches. Putting this into a ramdisk can boost performance.
58 58 ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space
59 59 #cache_dir = %(here)s/data
60 60
61 61 ; ***************************************
62 62 ; `repo_object` cache, default file based
63 63 ; ***************************************
64 64
65 65 ; `repo_object` cache settings for vcs methods for repositories
66 66 #rc_cache.repo_object.backend = dogpile.cache.rc.file_namespace
67 67
68 68 ; cache auto-expires after N seconds
69 69 ; Examples: 86400 (1Day), 604800 (7Days), 1209600 (14Days), 2592000 (30days), 7776000 (90Days)
70 70 #rc_cache.repo_object.expiration_time = 2592000
71 71
72 72 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
73 73 #rc_cache.repo_object.arguments.filename = /tmp/vcsserver_cache_repo_object.db
74 74
75 75 ; ***********************************************************
76 76 ; `repo_object` cache with redis backend
77 77 ; recommended for larger instance, and for better performance
78 78 ; ***********************************************************
79 79
80 80 ; `repo_object` cache settings for vcs methods for repositories
81 81 #rc_cache.repo_object.backend = dogpile.cache.rc.redis_msgpack
82 82
83 83 ; cache auto-expires after N seconds
84 84 ; Examples: 86400 (1Day), 604800 (7Days), 1209600 (14Days), 2592000 (30days), 7776000 (90Days)
85 85 #rc_cache.repo_object.expiration_time = 2592000
86 86
87 87 ; redis_expiration_time needs to be greater then expiration_time
88 88 #rc_cache.repo_object.arguments.redis_expiration_time = 3592000
89 89
90 90 #rc_cache.repo_object.arguments.host = localhost
91 91 #rc_cache.repo_object.arguments.port = 6379
92 92 #rc_cache.repo_object.arguments.db = 5
93 93 #rc_cache.repo_object.arguments.socket_timeout = 30
94 94 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
95 95 #rc_cache.repo_object.arguments.distributed_lock = true
96 96
97 97 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
98 98 #rc_cache.repo_object.arguments.lock_auto_renewal = true
99 99
100 100 ; Statsd client config, this is used to send metrics to statsd
101 101 ; We recommend setting statsd_exported and scrape them using Promethues
102 102 #statsd.enabled = false
103 103 #statsd.statsd_host = 0.0.0.0
104 104 #statsd.statsd_port = 8125
105 105 #statsd.statsd_prefix =
106 106 #statsd.statsd_ipv6 = false
107 107
108 108 ; configure logging automatically at server startup set to false
109 109 ; to use the below custom logging config.
110 110 ; RC_LOGGING_FORMATTER
111 111 ; RC_LOGGING_LEVEL
112 112 ; env variables can control the settings for logging in case of autoconfigure
113 113
114 114 #logging.autoconfigure = true
115 115
116 116 ; specify your own custom logging config file to configure logging
117 117 #logging.logging_conf_file = /path/to/custom_logging.ini
118 118
119 119 ; #####################
120 120 ; LOGGING CONFIGURATION
121 121 ; #####################
122 122
123 123 [loggers]
124 124 keys = root, vcsserver
125 125
126 126 [handlers]
127 127 keys = console
128 128
129 129 [formatters]
130 130 keys = generic, json
131 131
132 132 ; #######
133 133 ; LOGGERS
134 134 ; #######
135 135 [logger_root]
136 136 level = NOTSET
137 137 handlers = console
138 138
139 139 [logger_vcsserver]
140 140 level = INFO
141 141 handlers =
142 142 qualname = vcsserver
143 143 propagate = 1
144 144
145 145 ; ########
146 146 ; HANDLERS
147 147 ; ########
148 148
149 149 [handler_console]
150 150 class = StreamHandler
151 151 args = (sys.stderr, )
152 152 level = INFO
153 153 ; To enable JSON formatted logs replace 'generic' with 'json'
154 154 ; This allows sending properly formatted logs to grafana loki or elasticsearch
155 155 formatter = generic
156 156
157 157 ; ##########
158 158 ; FORMATTERS
159 159 ; ##########
160 160
161 161 [formatter_generic]
162 162 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
163 163 datefmt = %Y-%m-%d %H:%M:%S
164 164
165 165 [formatter_json]
166 166 format = %(timestamp)s %(levelname)s %(name)s %(message)s %(req_id)s
167 167 class = vcsserver.lib._vendor.jsonlogger.JsonFormatter
@@ -1,168 +1,184 b''
1 1 # Copyright (C) 2010-2023 RhodeCode GmbH
2 2 #
3 3 # This program is free software: you can redistribute it and/or modify
4 4 # it under the terms of the GNU Affero General Public License, version 3
5 5 # (only), as published by the Free Software Foundation.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU Affero General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 #
15 15 # This program is dual-licensed. If you wish to learn more about the
16 16 # RhodeCode Enterprise Edition, including its added features, Support services,
17 17 # and proprietary license terms, please see https://rhodecode.com/licenses/
18 18
19 19 import os
20 20 import textwrap
21 21 import string
22 22 import functools
23 23 import logging
24 24 import tempfile
25 25 import logging.config
26 26
27 27 from vcsserver.type_utils import str2bool, aslist
28 28
29 29 log = logging.getLogger(__name__)
30 30
31
31 32 # skip keys, that are set here, so we don't double process those
32 33 set_keys = {
33 34 '__file__': ''
34 35 }
35 36
36 37
37 38 class SettingsMaker:
38 39
39 40 def __init__(self, app_settings):
40 41 self.settings = app_settings
41 42
42 43 @classmethod
43 44 def _bool_func(cls, input_val):
44 45 if isinstance(input_val, bytes):
45 46 # decode to str
46 47 input_val = input_val.decode('utf8')
47 48 return str2bool(input_val)
48 49
49 50 @classmethod
50 51 def _int_func(cls, input_val):
51 52 return int(input_val)
52 53
53 54 @classmethod
55 def _float_func(cls, input_val):
56 return float(input_val)
57
58 @classmethod
54 59 def _list_func(cls, input_val, sep=','):
55 60 return aslist(input_val, sep=sep)
56 61
57 62 @classmethod
58 63 def _string_func(cls, input_val, lower=True):
59 64 if lower:
60 65 input_val = input_val.lower()
61 66 return input_val
62 67
63 68 @classmethod
64 def _float_func(cls, input_val):
65 return float(input_val)
69 def _string_no_quote_func(cls, input_val, lower=True):
70 """
71 Special case string function that detects if value is set to empty quote string
72 e.g.
73
74 core.binar_dir = ""
75 """
76
77 input_val = cls._string_func(input_val, lower=lower)
78 if input_val in ['""', "''"]:
79 return ''
66 80
67 81 @classmethod
68 82 def _dir_func(cls, input_val, ensure_dir=False, mode=0o755):
69 83
70 84 # ensure we have our dir created
71 85 if not os.path.isdir(input_val) and ensure_dir:
72 86 os.makedirs(input_val, mode=mode, exist_ok=True)
73 87
74 88 if not os.path.isdir(input_val):
75 89 raise Exception(f'Dir at {input_val} does not exist')
76 90 return input_val
77 91
78 92 @classmethod
79 93 def _file_path_func(cls, input_val, ensure_dir=False, mode=0o755):
80 94 dirname = os.path.dirname(input_val)
81 95 cls._dir_func(dirname, ensure_dir=ensure_dir)
82 96 return input_val
83 97
84 98 @classmethod
85 99 def _key_transformator(cls, key):
86 100 return "{}_{}".format('RC'.upper(), key.upper().replace('.', '_').replace('-', '_'))
87 101
88 102 def maybe_env_key(self, key):
89 103 # now maybe we have this KEY in env, search and use the value with higher priority.
90 104 transformed_key = self._key_transformator(key)
91 105 envvar_value = os.environ.get(transformed_key)
92 106 if envvar_value:
93 107 log.debug('using `%s` key instead of `%s` key for config', transformed_key, key)
94 108
95 109 return envvar_value
96 110
97 111 def env_expand(self):
98 112 replaced = {}
99 113 for k, v in self.settings.items():
100 114 if k not in set_keys:
101 115 envvar_value = self.maybe_env_key(k)
102 116 if envvar_value:
103 117 replaced[k] = envvar_value
104 118 set_keys[k] = envvar_value
105 119
106 120 # replace ALL keys updated
107 121 self.settings.update(replaced)
108 122
109 123 def enable_logging(self, logging_conf=None, level='INFO', formatter='generic'):
110 124 """
111 125 Helper to enable debug on running instance
112 126 :return:
113 127 """
114 128
115 129 if not str2bool(self.settings.get('logging.autoconfigure')):
116 130 log.info('logging configuration based on main .ini file')
117 131 return
118 132
119 133 if logging_conf is None:
120 134 logging_conf = self.settings.get('logging.logging_conf_file') or ''
121 135
122 136 if not os.path.isfile(logging_conf):
123 137 log.error('Unable to setup logging based on %s, '
124 138 'file does not exist.... specify path using logging.logging_conf_file= config setting. ', logging_conf)
125 139 return
126 140
127 141 with open(logging_conf, 'rt') as f:
128 142 ini_template = textwrap.dedent(f.read())
129 143 ini_template = string.Template(ini_template).safe_substitute(
130 144 RC_LOGGING_LEVEL=os.environ.get('RC_LOGGING_LEVEL', '') or level,
131 145 RC_LOGGING_FORMATTER=os.environ.get('RC_LOGGING_FORMATTER', '') or formatter
132 146 )
133 147
134 148 with tempfile.NamedTemporaryFile(prefix='rc_logging_', suffix='.ini', delete=False) as f:
135 149 log.info('Saved Temporary LOGGING config at %s', f.name)
136 150 f.write(ini_template)
137 151
138 152 logging.config.fileConfig(f.name)
139 153 os.remove(f.name)
140 154
141 155 def make_setting(self, key, default, lower=False, default_when_empty=False, parser=None):
142 156 input_val = self.settings.get(key, default)
143 157
144 158 if default_when_empty and not input_val:
145 159 # use default value when value is set in the config but it is empty
146 160 input_val = default
147 161
148 162 parser_func = {
149 163 'bool': self._bool_func,
150 164 'int': self._int_func,
165 'float': self._float_func,
151 166 'list': self._list_func,
152 167 'list:newline': functools.partial(self._list_func, sep='/n'),
153 168 'list:spacesep': functools.partial(self._list_func, sep=' '),
154 169 'string': functools.partial(self._string_func, lower=lower),
170 'string:noquote': functools.partial(self._string_no_quote_func, lower=lower),
155 171 'dir': self._dir_func,
156 172 'dir:ensured': functools.partial(self._dir_func, ensure_dir=True),
157 173 'file': self._file_path_func,
158 174 'file:ensured': functools.partial(self._file_path_func, ensure_dir=True),
159 175 None: lambda i: i
160 176 }[parser]
161 177
162 178 envvar_value = self.maybe_env_key(key)
163 179 if envvar_value:
164 180 input_val = envvar_value
165 181 set_keys[key] = input_val
166 182
167 183 self.settings[key] = parser_func(input_val)
168 184 return self.settings[key]
@@ -1,777 +1,777 b''
1 1 # RhodeCode VCSServer provides access to different vcs backends via network.
2 2 # Copyright (C) 2014-2023 RhodeCode GmbH
3 3 #
4 4 # This program is free software; you can redistribute it and/or modify
5 5 # it under the terms of the GNU General Public License as published by
6 6 # the Free Software Foundation; either version 3 of the License, or
7 7 # (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software Foundation,
16 16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 17
18 18 import io
19 19 import os
20 20 import platform
21 21 import sys
22 22 import locale
23 23 import logging
24 24 import uuid
25 25 import time
26 26 import wsgiref.util
27 27 import tempfile
28 28 import psutil
29 29
30 30 from itertools import chain
31 31
32 32 import msgpack
33 33 import configparser
34 34
35 35 from pyramid.config import Configurator
36 36 from pyramid.wsgi import wsgiapp
37 37 from pyramid.response import Response
38 38
39 39 from vcsserver.base import BytesEnvelope, BinaryEnvelope
40 40 from vcsserver.lib.rc_json import json
41 41 from vcsserver.config.settings_maker import SettingsMaker
42 42 from vcsserver.str_utils import safe_int
43 43 from vcsserver.lib.statsd_client import StatsdClient
44 44 from vcsserver.tweens.request_wrapper import get_headers_call_context
45 45
46 46 import vcsserver
47 47 from vcsserver import remote_wsgi, scm_app, settings, hgpatches
48 48 from vcsserver.git_lfs.app import GIT_LFS_CONTENT_TYPE, GIT_LFS_PROTO_PAT
49 49 from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub
50 50 from vcsserver.echo_stub.echo_app import EchoApp
51 51 from vcsserver.exceptions import HTTPRepoLocked, HTTPRepoBranchProtected
52 52 from vcsserver.lib.exc_tracking import store_exception, format_exc
53 53 from vcsserver.server import VcsServer
54 54
55 55 strict_vcs = True
56 56
57 57 git_import_err = None
58 58 try:
59 59 from vcsserver.remote.git_remote import GitFactory, GitRemote
60 60 except ImportError as e:
61 61 GitFactory = None
62 62 GitRemote = None
63 63 git_import_err = e
64 64 if strict_vcs:
65 65 raise
66 66
67 67
68 68 hg_import_err = None
69 69 try:
70 70 from vcsserver.remote.hg_remote import MercurialFactory, HgRemote
71 71 except ImportError as e:
72 72 MercurialFactory = None
73 73 HgRemote = None
74 74 hg_import_err = e
75 75 if strict_vcs:
76 76 raise
77 77
78 78
79 79 svn_import_err = None
80 80 try:
81 81 from vcsserver.remote.svn_remote import SubversionFactory, SvnRemote
82 82 except ImportError as e:
83 83 SubversionFactory = None
84 84 SvnRemote = None
85 85 svn_import_err = e
86 86 if strict_vcs:
87 87 raise
88 88
89 89 log = logging.getLogger(__name__)
90 90
91 91 # due to Mercurial/glibc2.27 problems we need to detect if locale settings are
92 92 # causing problems and "fix" it in case they do and fallback to LC_ALL = C
93 93
94 94 try:
95 95 locale.setlocale(locale.LC_ALL, '')
96 96 except locale.Error as e:
97 97 log.error(
98 98 'LOCALE ERROR: failed to set LC_ALL, fallback to LC_ALL=C, org error: %s', e)
99 99 os.environ['LC_ALL'] = 'C'
100 100
101 101
102 102 def _is_request_chunked(environ):
103 103 stream = environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked'
104 104 return stream
105 105
106 106
107 107 def log_max_fd():
108 108 try:
109 109 maxfd = psutil.Process().rlimit(psutil.RLIMIT_NOFILE)[1]
110 110 log.info('Max file descriptors value: %s', maxfd)
111 111 except Exception:
112 112 pass
113 113
114 114
115 115 class VCS:
116 116 def __init__(self, locale_conf=None, cache_config=None):
117 117 self.locale = locale_conf
118 118 self.cache_config = cache_config
119 119 self._configure_locale()
120 120
121 121 log_max_fd()
122 122
123 123 if GitFactory and GitRemote:
124 124 git_factory = GitFactory()
125 125 self._git_remote = GitRemote(git_factory)
126 126 else:
127 127 log.error("Git client import failed: %s", git_import_err)
128 128
129 129 if MercurialFactory and HgRemote:
130 130 hg_factory = MercurialFactory()
131 131 self._hg_remote = HgRemote(hg_factory)
132 132 else:
133 133 log.error("Mercurial client import failed: %s", hg_import_err)
134 134
135 135 if SubversionFactory and SvnRemote:
136 136 svn_factory = SubversionFactory()
137 137
138 138 # hg factory is used for svn url validation
139 139 hg_factory = MercurialFactory()
140 140 self._svn_remote = SvnRemote(svn_factory, hg_factory=hg_factory)
141 141 else:
142 142 log.error("Subversion client import failed: %s", svn_import_err)
143 143
144 144 self._vcsserver = VcsServer()
145 145
146 146 def _configure_locale(self):
147 147 if self.locale:
148 148 log.info('Settings locale: `LC_ALL` to %s', self.locale)
149 149 else:
150 150 log.info('Configuring locale subsystem based on environment variables')
151 151 try:
152 152 # If self.locale is the empty string, then the locale
153 153 # module will use the environment variables. See the
154 154 # documentation of the package `locale`.
155 155 locale.setlocale(locale.LC_ALL, self.locale)
156 156
157 157 language_code, encoding = locale.getlocale()
158 158 log.info(
159 159 'Locale set to language code "%s" with encoding "%s".',
160 160 language_code, encoding)
161 161 except locale.Error:
162 162 log.exception('Cannot set locale, not configuring the locale system')
163 163
164 164
165 165 class WsgiProxy:
166 166 def __init__(self, wsgi):
167 167 self.wsgi = wsgi
168 168
169 169 def __call__(self, environ, start_response):
170 170 input_data = environ['wsgi.input'].read()
171 171 input_data = msgpack.unpackb(input_data)
172 172
173 173 error = None
174 174 try:
175 175 data, status, headers = self.wsgi.handle(
176 176 input_data['environment'], input_data['input_data'],
177 177 *input_data['args'], **input_data['kwargs'])
178 178 except Exception as e:
179 179 data, status, headers = [], None, None
180 180 error = {
181 181 'message': str(e),
182 182 '_vcs_kind': getattr(e, '_vcs_kind', None)
183 183 }
184 184
185 185 start_response(200, {})
186 186 return self._iterator(error, status, headers, data)
187 187
188 188 def _iterator(self, error, status, headers, data):
189 189 initial_data = [
190 190 error,
191 191 status,
192 192 headers,
193 193 ]
194 194
195 195 for d in chain(initial_data, data):
196 196 yield msgpack.packb(d)
197 197
198 198
199 199 def not_found(request):
200 200 return {'status': '404 NOT FOUND'}
201 201
202 202
203 203 class VCSViewPredicate:
204 204 def __init__(self, val, config):
205 205 self.remotes = val
206 206
207 207 def text(self):
208 208 return f'vcs view method = {list(self.remotes.keys())}'
209 209
210 210 phash = text
211 211
212 212 def __call__(self, context, request):
213 213 """
214 214 View predicate that returns true if given backend is supported by
215 215 defined remotes.
216 216 """
217 217 backend = request.matchdict.get('backend')
218 218 return backend in self.remotes
219 219
220 220
221 221 class HTTPApplication:
222 222 ALLOWED_EXCEPTIONS = ('KeyError', 'URLError')
223 223
224 224 remote_wsgi = remote_wsgi
225 225 _use_echo_app = False
226 226
227 227 def __init__(self, settings=None, global_config=None):
228 228
229 229 self.config = Configurator(settings=settings)
230 230 # Init our statsd at very start
231 231 self.config.registry.statsd = StatsdClient.statsd
232 232 self.config.registry.vcs_call_context = {}
233 233
234 234 self.global_config = global_config
235 235 self.config.include('vcsserver.lib.rc_cache')
236 236 self.config.include('vcsserver.lib.rc_cache.archive_cache')
237 237
238 238 settings_locale = settings.get('locale', '') or 'en_US.UTF-8'
239 239 vcs = VCS(locale_conf=settings_locale, cache_config=settings)
240 240 self._remotes = {
241 241 'hg': vcs._hg_remote,
242 242 'git': vcs._git_remote,
243 243 'svn': vcs._svn_remote,
244 244 'server': vcs._vcsserver,
245 245 }
246 246 if settings.get('dev.use_echo_app', 'false').lower() == 'true':
247 247 self._use_echo_app = True
248 248 log.warning("Using EchoApp for VCS operations.")
249 249 self.remote_wsgi = remote_wsgi_stub
250 250
251 251 self._configure_settings(global_config, settings)
252 252
253 253 self._configure()
254 254
255 255 def _configure_settings(self, global_config, app_settings):
256 256 """
257 257 Configure the settings module.
258 258 """
259 259 settings_merged = global_config.copy()
260 260 settings_merged.update(app_settings)
261 261
262 262 binary_dir = app_settings['core.binary_dir']
263 263
264 264 settings.BINARY_DIR = binary_dir
265 265
266 266 # from core.binary dir we set executable paths
267 267 settings.GIT_EXECUTABLE = os.path.join(binary_dir, settings.GIT_EXECUTABLE)
268 268 settings.SVN_EXECUTABLE = os.path.join(binary_dir, settings.SVN_EXECUTABLE)
269 269 settings.SVNLOOK_EXECUTABLE = os.path.join(binary_dir, settings.SVNLOOK_EXECUTABLE)
270 270
271 271 # Store the settings to make them available to other modules.
272 272 vcsserver.PYRAMID_SETTINGS = settings_merged
273 273 vcsserver.CONFIG = settings_merged
274 274
275 275 def _configure(self):
276 276 self.config.add_renderer(name='msgpack', factory=self._msgpack_renderer_factory)
277 277
278 278 self.config.add_route('service', '/_service')
279 279 self.config.add_route('status', '/status')
280 280 self.config.add_route('hg_proxy', '/proxy/hg')
281 281 self.config.add_route('git_proxy', '/proxy/git')
282 282
283 283 # rpc methods
284 284 self.config.add_route('vcs', '/{backend}')
285 285
286 286 # streaming rpc remote methods
287 287 self.config.add_route('vcs_stream', '/{backend}/stream')
288 288
289 289 # vcs operations clone/push as streaming
290 290 self.config.add_route('stream_git', '/stream/git/*repo_name')
291 291 self.config.add_route('stream_hg', '/stream/hg/*repo_name')
292 292
293 293 self.config.add_view(self.status_view, route_name='status', renderer='json')
294 294 self.config.add_view(self.service_view, route_name='service', renderer='msgpack')
295 295
296 296 self.config.add_view(self.hg_proxy(), route_name='hg_proxy')
297 297 self.config.add_view(self.git_proxy(), route_name='git_proxy')
298 298 self.config.add_view(self.vcs_view, route_name='vcs', renderer='msgpack',
299 299 vcs_view=self._remotes)
300 300 self.config.add_view(self.vcs_stream_view, route_name='vcs_stream',
301 301 vcs_view=self._remotes)
302 302
303 303 self.config.add_view(self.hg_stream(), route_name='stream_hg')
304 304 self.config.add_view(self.git_stream(), route_name='stream_git')
305 305
306 306 self.config.add_view_predicate('vcs_view', VCSViewPredicate)
307 307
308 308 self.config.add_notfound_view(not_found, renderer='json')
309 309
310 310 self.config.add_view(self.handle_vcs_exception, context=Exception)
311 311
312 312 self.config.add_tween(
313 313 'vcsserver.tweens.request_wrapper.RequestWrapperTween',
314 314 )
315 315 self.config.add_request_method(
316 316 'vcsserver.lib.request_counter.get_request_counter',
317 317 'request_count')
318 318
319 319 def wsgi_app(self):
320 320 return self.config.make_wsgi_app()
321 321
322 322 def _vcs_view_params(self, request):
323 323 remote = self._remotes[request.matchdict['backend']]
324 324 payload = msgpack.unpackb(request.body, use_list=True)
325 325
326 326 method = payload.get('method')
327 327 params = payload['params']
328 328 wire = params.get('wire')
329 329 args = params.get('args')
330 330 kwargs = params.get('kwargs')
331 331 context_uid = None
332 332
333 333 request.registry.vcs_call_context = {
334 334 'method': method,
335 335 'repo_name': payload.get('_repo_name'),
336 336 }
337 337
338 338 if wire:
339 339 try:
340 340 wire['context'] = context_uid = uuid.UUID(wire['context'])
341 341 except KeyError:
342 342 pass
343 343 args.insert(0, wire)
344 344 repo_state_uid = wire.get('repo_state_uid') if wire else None
345 345
346 346 # NOTE(marcink): trading complexity for slight performance
347 347 if log.isEnabledFor(logging.DEBUG):
348 348 # also we SKIP printing out any of those methods args since they maybe excessive
349 349 just_args_methods = {
350 350 'commitctx': ('content', 'removed', 'updated'),
351 351 'commit': ('content', 'removed', 'updated')
352 352 }
353 353 if method in just_args_methods:
354 354 skip_args = just_args_methods[method]
355 355 call_args = ''
356 356 call_kwargs = {}
357 357 for k in kwargs:
358 358 if k in skip_args:
359 359 # replace our skip key with dummy
360 360 call_kwargs[k] = f'RemovedParam({k})'
361 361 else:
362 362 call_kwargs[k] = kwargs[k]
363 363 else:
364 364 call_args = args[1:]
365 365 call_kwargs = kwargs
366 366
367 367 log.debug('Method requested:`%s` with args:%s kwargs:%s context_uid: %s, repo_state_uid:%s',
368 368 method, call_args, call_kwargs, context_uid, repo_state_uid)
369 369
370 370 statsd = request.registry.statsd
371 371 if statsd:
372 372 statsd.incr(
373 373 'vcsserver_method_total', tags=[
374 374 f"method:{method}",
375 375 ])
376 376 return payload, remote, method, args, kwargs
377 377
378 378 def vcs_view(self, request):
379 379
380 380 payload, remote, method, args, kwargs = self._vcs_view_params(request)
381 381 payload_id = payload.get('id')
382 382
383 383 try:
384 384 resp = getattr(remote, method)(*args, **kwargs)
385 385 except Exception as e:
386 386 exc_info = list(sys.exc_info())
387 387 exc_type, exc_value, exc_traceback = exc_info
388 388
389 389 org_exc = getattr(e, '_org_exc', None)
390 390 org_exc_name = None
391 391 org_exc_tb = ''
392 392 if org_exc:
393 393 org_exc_name = org_exc.__class__.__name__
394 394 org_exc_tb = getattr(e, '_org_exc_tb', '')
395 395 # replace our "faked" exception with our org
396 396 exc_info[0] = org_exc.__class__
397 397 exc_info[1] = org_exc
398 398
399 399 should_store_exc = True
400 400 if org_exc:
401 401 def get_exc_fqn(_exc_obj):
402 402 module_name = getattr(org_exc.__class__, '__module__', 'UNKNOWN')
403 403 return module_name + '.' + org_exc_name
404 404
405 405 exc_fqn = get_exc_fqn(org_exc)
406 406
407 407 if exc_fqn in ['mercurial.error.RepoLookupError',
408 408 'vcsserver.exceptions.RefNotFoundException']:
409 409 should_store_exc = False
410 410
411 411 if should_store_exc:
412 412 store_exception(id(exc_info), exc_info, request_path=request.path)
413 413
414 414 tb_info = format_exc(exc_info)
415 415
416 416 type_ = e.__class__.__name__
417 417 if type_ not in self.ALLOWED_EXCEPTIONS:
418 418 type_ = None
419 419
420 420 resp = {
421 421 'id': payload_id,
422 422 'error': {
423 423 'message': str(e),
424 424 'traceback': tb_info,
425 425 'org_exc': org_exc_name,
426 426 'org_exc_tb': org_exc_tb,
427 427 'type': type_
428 428 }
429 429 }
430 430
431 431 try:
432 432 resp['error']['_vcs_kind'] = getattr(e, '_vcs_kind', None)
433 433 except AttributeError:
434 434 pass
435 435 else:
436 436 resp = {
437 437 'id': payload_id,
438 438 'result': resp
439 439 }
440 440 log.debug('Serving data for method %s', method)
441 441 return resp
442 442
443 443 def vcs_stream_view(self, request):
444 444 payload, remote, method, args, kwargs = self._vcs_view_params(request)
445 445 # this method has a stream: marker we remove it here
446 446 method = method.split('stream:')[-1]
447 447 chunk_size = safe_int(payload.get('chunk_size')) or 4096
448 448
449 449 resp = getattr(remote, method)(*args, **kwargs)
450 450
451 451 def get_chunked_data(method_resp):
452 452 stream = io.BytesIO(method_resp)
453 453 while 1:
454 454 chunk = stream.read(chunk_size)
455 455 if not chunk:
456 456 break
457 457 yield chunk
458 458
459 459 response = Response(app_iter=get_chunked_data(resp))
460 460 response.content_type = 'application/octet-stream'
461 461
462 462 return response
463 463
464 464 def status_view(self, request):
465 465 import vcsserver
466 466 _platform_id = platform.uname()[1] or 'instance'
467 467
468 468 return {
469 469 "status": "OK",
470 470 "vcsserver_version": vcsserver.get_version(),
471 471 "platform": _platform_id,
472 472 "pid": os.getpid(),
473 473 }
474 474
475 475 def service_view(self, request):
476 476 import vcsserver
477 477
478 478 payload = msgpack.unpackb(request.body, use_list=True)
479 479 server_config, app_config = {}, {}
480 480
481 481 try:
482 482 path = self.global_config['__file__']
483 483 config = configparser.RawConfigParser()
484 484
485 485 config.read(path)
486 486
487 487 if config.has_section('server:main'):
488 488 server_config = dict(config.items('server:main'))
489 489 if config.has_section('app:main'):
490 490 app_config = dict(config.items('app:main'))
491 491
492 492 except Exception:
493 493 log.exception('Failed to read .ini file for display')
494 494
495 495 environ = list(os.environ.items())
496 496
497 497 resp = {
498 498 'id': payload.get('id'),
499 499 'result': dict(
500 500 version=vcsserver.get_version(),
501 501 config=server_config,
502 502 app_config=app_config,
503 503 environ=environ,
504 504 payload=payload,
505 505 )
506 506 }
507 507 return resp
508 508
509 509 def _msgpack_renderer_factory(self, info):
510 510
511 511 def _render(value, system):
512 512 bin_type = False
513 513 res = value.get('result')
514 514 if isinstance(res, BytesEnvelope):
515 515 log.debug('Result is wrapped in BytesEnvelope type')
516 516 bin_type = True
517 517 elif isinstance(res, BinaryEnvelope):
518 518 log.debug('Result is wrapped in BinaryEnvelope type')
519 519 value['result'] = res.val
520 520 bin_type = True
521 521
522 522 request = system.get('request')
523 523 if request is not None:
524 524 response = request.response
525 525 ct = response.content_type
526 526 if ct == response.default_content_type:
527 527 response.content_type = 'application/x-msgpack'
528 528 if bin_type:
529 529 response.content_type = 'application/x-msgpack-bin'
530 530
531 531 return msgpack.packb(value, use_bin_type=bin_type)
532 532 return _render
533 533
534 534 def set_env_from_config(self, environ, config):
535 535 dict_conf = {}
536 536 try:
537 537 for elem in config:
538 538 if elem[0] == 'rhodecode':
539 539 dict_conf = json.loads(elem[2])
540 540 break
541 541 except Exception:
542 542 log.exception('Failed to fetch SCM CONFIG')
543 543 return
544 544
545 545 username = dict_conf.get('username')
546 546 if username:
547 547 environ['REMOTE_USER'] = username
548 548 # mercurial specific, some extension api rely on this
549 549 environ['HGUSER'] = username
550 550
551 551 ip = dict_conf.get('ip')
552 552 if ip:
553 553 environ['REMOTE_HOST'] = ip
554 554
555 555 if _is_request_chunked(environ):
556 556 # set the compatibility flag for webob
557 557 environ['wsgi.input_terminated'] = True
558 558
559 559 def hg_proxy(self):
560 560 @wsgiapp
561 561 def _hg_proxy(environ, start_response):
562 562 app = WsgiProxy(self.remote_wsgi.HgRemoteWsgi())
563 563 return app(environ, start_response)
564 564 return _hg_proxy
565 565
566 566 def git_proxy(self):
567 567 @wsgiapp
568 568 def _git_proxy(environ, start_response):
569 569 app = WsgiProxy(self.remote_wsgi.GitRemoteWsgi())
570 570 return app(environ, start_response)
571 571 return _git_proxy
572 572
573 573 def hg_stream(self):
574 574 if self._use_echo_app:
575 575 @wsgiapp
576 576 def _hg_stream(environ, start_response):
577 577 app = EchoApp('fake_path', 'fake_name', None)
578 578 return app(environ, start_response)
579 579 return _hg_stream
580 580 else:
581 581 @wsgiapp
582 582 def _hg_stream(environ, start_response):
583 583 log.debug('http-app: handling hg stream')
584 584 call_context = get_headers_call_context(environ)
585 585
586 586 repo_path = call_context['repo_path']
587 587 repo_name = call_context['repo_name']
588 588 config = call_context['repo_config']
589 589
590 590 app = scm_app.create_hg_wsgi_app(
591 591 repo_path, repo_name, config)
592 592
593 593 # Consistent path information for hgweb
594 594 environ['PATH_INFO'] = call_context['path_info']
595 595 environ['REPO_NAME'] = repo_name
596 596 self.set_env_from_config(environ, config)
597 597
598 598 log.debug('http-app: starting app handler '
599 599 'with %s and process request', app)
600 600 return app(environ, ResponseFilter(start_response))
601 601 return _hg_stream
602 602
603 603 def git_stream(self):
604 604 if self._use_echo_app:
605 605 @wsgiapp
606 606 def _git_stream(environ, start_response):
607 607 app = EchoApp('fake_path', 'fake_name', None)
608 608 return app(environ, start_response)
609 609 return _git_stream
610 610 else:
611 611 @wsgiapp
612 612 def _git_stream(environ, start_response):
613 613 log.debug('http-app: handling git stream')
614 614
615 615 call_context = get_headers_call_context(environ)
616 616
617 617 repo_path = call_context['repo_path']
618 618 repo_name = call_context['repo_name']
619 619 config = call_context['repo_config']
620 620
621 621 environ['PATH_INFO'] = call_context['path_info']
622 622 self.set_env_from_config(environ, config)
623 623
624 624 content_type = environ.get('CONTENT_TYPE', '')
625 625
626 626 path = environ['PATH_INFO']
627 627 is_lfs_request = GIT_LFS_CONTENT_TYPE in content_type
628 628 log.debug(
629 629 'LFS: Detecting if request `%s` is LFS server path based '
630 630 'on content type:`%s`, is_lfs:%s',
631 631 path, content_type, is_lfs_request)
632 632
633 633 if not is_lfs_request:
634 634 # fallback detection by path
635 635 if GIT_LFS_PROTO_PAT.match(path):
636 636 is_lfs_request = True
637 637 log.debug(
638 638 'LFS: fallback detection by path of: `%s`, is_lfs:%s',
639 639 path, is_lfs_request)
640 640
641 641 if is_lfs_request:
642 642 app = scm_app.create_git_lfs_wsgi_app(
643 643 repo_path, repo_name, config)
644 644 else:
645 645 app = scm_app.create_git_wsgi_app(
646 646 repo_path, repo_name, config)
647 647
648 648 log.debug('http-app: starting app handler '
649 649 'with %s and process request', app)
650 650
651 651 return app(environ, start_response)
652 652
653 653 return _git_stream
654 654
655 655 def handle_vcs_exception(self, exception, request):
656 656 _vcs_kind = getattr(exception, '_vcs_kind', '')
657 657
658 658 if _vcs_kind == 'repo_locked':
659 659 headers_call_context = get_headers_call_context(request.environ)
660 660 status_code = safe_int(headers_call_context['locked_status_code'])
661 661
662 662 return HTTPRepoLocked(
663 663 title=str(exception), status_code=status_code, headers=[('X-Rc-Locked', '1')])
664 664
665 665 elif _vcs_kind == 'repo_branch_protected':
666 666 # Get custom repo-branch-protected status code if present.
667 667 return HTTPRepoBranchProtected(
668 668 title=str(exception), headers=[('X-Rc-Branch-Protection', '1')])
669 669
670 670 exc_info = request.exc_info
671 671 store_exception(id(exc_info), exc_info)
672 672
673 673 traceback_info = 'unavailable'
674 674 if request.exc_info:
675 675 traceback_info = format_exc(request.exc_info)
676 676
677 677 log.error(
678 678 'error occurred handling this request for path: %s, \n%s',
679 679 request.path, traceback_info)
680 680
681 681 statsd = request.registry.statsd
682 682 if statsd:
683 683 exc_type = f"{exception.__class__.__module__}.{exception.__class__.__name__}"
684 684 statsd.incr('vcsserver_exception_total',
685 685 tags=[f"type:{exc_type}"])
686 686 raise exception
687 687
688 688
689 689 class ResponseFilter:
690 690
691 691 def __init__(self, start_response):
692 692 self._start_response = start_response
693 693
694 694 def __call__(self, status, response_headers, exc_info=None):
695 695 headers = tuple(
696 696 (h, v) for h, v in response_headers
697 697 if not wsgiref.util.is_hop_by_hop(h))
698 698 return self._start_response(status, headers, exc_info)
699 699
700 700
701 701 def sanitize_settings_and_apply_defaults(global_config, settings):
702 702 _global_settings_maker = SettingsMaker(global_config)
703 703 settings_maker = SettingsMaker(settings)
704 704
705 705 settings_maker.make_setting('logging.autoconfigure', False, parser='bool')
706 706
707 707 logging_conf = os.path.join(os.path.dirname(global_config.get('__file__')), 'logging.ini')
708 708 settings_maker.enable_logging(logging_conf)
709 709
710 710 # Default includes, possible to change as a user
711 711 pyramid_includes = settings_maker.make_setting('pyramid.includes', [], parser='list:newline')
712 712 log.debug("Using the following pyramid.includes: %s", pyramid_includes)
713 713
714 714 settings_maker.make_setting('__file__', global_config.get('__file__'))
715 715
716 716 settings_maker.make_setting('pyramid.default_locale_name', 'en')
717 717 settings_maker.make_setting('locale', 'en_US.UTF-8')
718 718
719 settings_maker.make_setting('core.binary_dir', '/usr/local/bin/rhodecode_bin/vcs_bin')
719 settings_maker.make_setting('core.binary_dir', '/usr/local/bin/rhodecode_bin/vcs_bin', parser='string:noquote')
720 720
721 721 temp_store = tempfile.gettempdir()
722 722 default_cache_dir = os.path.join(temp_store, 'rc_cache')
723 723 # save default, cache dir, and use it for all backends later.
724 724 default_cache_dir = settings_maker.make_setting(
725 725 'cache_dir',
726 726 default=default_cache_dir, default_when_empty=True,
727 727 parser='dir:ensured')
728 728
729 729 # exception store cache
730 730 settings_maker.make_setting(
731 731 'exception_tracker.store_path',
732 732 default=os.path.join(default_cache_dir, 'exc_store'), default_when_empty=True,
733 733 parser='dir:ensured'
734 734 )
735 735
736 736 # repo_object cache defaults
737 737 settings_maker.make_setting(
738 738 'rc_cache.repo_object.backend',
739 739 default='dogpile.cache.rc.file_namespace',
740 740 parser='string')
741 741 settings_maker.make_setting(
742 742 'rc_cache.repo_object.expiration_time',
743 743 default=30 * 24 * 60 * 60, # 30days
744 744 parser='int')
745 745 settings_maker.make_setting(
746 746 'rc_cache.repo_object.arguments.filename',
747 747 default=os.path.join(default_cache_dir, 'vcsserver_cache_repo_object.db'),
748 748 parser='string')
749 749
750 750 # statsd
751 751 settings_maker.make_setting('statsd.enabled', False, parser='bool')
752 752 settings_maker.make_setting('statsd.statsd_host', 'statsd-exporter', parser='string')
753 753 settings_maker.make_setting('statsd.statsd_port', 9125, parser='int')
754 754 settings_maker.make_setting('statsd.statsd_prefix', '')
755 755 settings_maker.make_setting('statsd.statsd_ipv6', False, parser='bool')
756 756
757 757 settings_maker.env_expand()
758 758
759 759
760 760 def main(global_config, **settings):
761 761 start_time = time.time()
762 762 log.info('Pyramid app config starting')
763 763
764 764 if MercurialFactory:
765 765 hgpatches.patch_largefiles_capabilities()
766 766 hgpatches.patch_subrepo_type_mapping()
767 767
768 768 # Fill in and sanitize the defaults & do ENV expansion
769 769 sanitize_settings_and_apply_defaults(global_config, settings)
770 770
771 771 # init and bootstrap StatsdClient
772 772 StatsdClient.setup(settings)
773 773
774 774 pyramid_app = HTTPApplication(settings=settings, global_config=global_config).wsgi_app()
775 775 total_time = time.time() - start_time
776 776 log.info('Pyramid app created and configured in %.2fs', total_time)
777 777 return pyramid_app
General Comments 0
You need to be logged in to leave comments. Login now