##// END OF EJS Templates
config: major update for the code to make it be almost fully controllable via env for new docker based installer.
super-admin -
r4823:59ec40ea default
parent child Browse files
Show More
@@ -0,0 +1,98 b''
1 ; #####################
2 ; LOGGING CONFIGURATION
3 ; #####################
4 [loggers]
5 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
6
7 [handlers]
8 keys = console, console_sql
9
10 [formatters]
11 keys = generic, json, color_formatter, color_formatter_sql
12
13 ; #######
14 ; LOGGERS
15 ; #######
16 [logger_root]
17 level = NOTSET
18 handlers = console
19
20 [logger_sqlalchemy]
21 level = $RC_LOGGING_LEVEL
22 handlers = console_sql
23 qualname = sqlalchemy.engine
24 propagate = 0
25
26 [logger_beaker]
27 level = $RC_LOGGING_LEVEL
28 handlers =
29 qualname = beaker.container
30 propagate = 1
31
32 [logger_rhodecode]
33 level = $RC_LOGGING_LEVEL
34 handlers =
35 qualname = rhodecode
36 propagate = 1
37
38 [logger_ssh_wrapper]
39 level = $RC_LOGGING_LEVEL
40 handlers =
41 qualname = ssh_wrapper
42 propagate = 1
43
44 [logger_celery]
45 level = $RC_LOGGING_LEVEL
46 handlers =
47 qualname = celery
48
49
50 ; ########
51 ; HANDLERS
52 ; ########
53
54 [handler_console]
55 class = StreamHandler
56 args = (sys.stderr, )
57 level = $RC_LOGGING_LEVEL
58 ; To enable JSON formatted logs replace generic with json
59 ; This allows sending properly formatted logs to grafana loki or elasticsearch
60 #formatter = json
61 #formatter = generic
62 formatter = $RC_LOGGING_FORMATTER
63
64 [handler_console_sql]
65 ; "level = DEBUG" logs SQL queries and results.
66 ; "level = INFO" logs SQL queries.
67 ; "level = WARN" logs neither. (Recommended for production systems.)
68 class = StreamHandler
69 args = (sys.stderr, )
70 level = WARN
71 ; To enable JSON formatted logs replace generic with json
72 ; This allows sending properly formatted logs to grafana loki or elasticsearch
73 #formatter = json
74 #formatter = generic
75 formatter = $RC_LOGGING_FORMATTER
76
77 ; ##########
78 ; FORMATTERS
79 ; ##########
80
81 [formatter_generic]
82 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
83 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
84 datefmt = %Y-%m-%d %H:%M:%S
85
86 [formatter_color_formatter]
87 class = rhodecode.lib.logging_formatter.ColorFormatter
88 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
89 datefmt = %Y-%m-%d %H:%M:%S
90
91 [formatter_color_formatter_sql]
92 class = rhodecode.lib.logging_formatter.ColorFormatterSql
93 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
94 datefmt = %Y-%m-%d %H:%M:%S
95
96 [formatter_json]
97 format = %(message)s
98 class = rhodecode.lib._vendor.jsonlogger.JsonFormatter No newline at end of file
@@ -0,0 +1,182 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import os
22 import textwrap
23 import string
24 import functools
25 import logging
26 import tempfile
27 import logging.config
28 log = logging.getLogger(__name__)
29
30
31 def str2bool(_str):
32 """
33 returns True/False value from given string, it tries to translate the
34 string into boolean
35
36 :param _str: string value to translate into boolean
37 :rtype: boolean
38 :returns: boolean from given string
39 """
40 if _str is None:
41 return False
42 if _str in (True, False):
43 return _str
44 _str = str(_str).strip().lower()
45 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
46
47
48 def aslist(obj, sep=None, strip=True):
49 """
50 Returns given string separated by sep as list
51
52 :param obj:
53 :param sep:
54 :param strip:
55 """
56 if isinstance(obj, (basestring,)):
57 if obj in ['', ""]:
58 return []
59
60 lst = obj.split(sep)
61 if strip:
62 lst = [v.strip() for v in lst]
63 return lst
64 elif isinstance(obj, (list, tuple)):
65 return obj
66 elif obj is None:
67 return []
68 else:
69 return [obj]
70
71
72 class SettingsMaker(object):
73
74 def __init__(self, app_settings):
75 self.settings = app_settings
76
77 @classmethod
78 def _bool_func(cls, input_val):
79 if isinstance(input_val, unicode):
80 input_val = input_val.encode('utf8')
81 return str2bool(input_val)
82
83 @classmethod
84 def _int_func(cls, input_val):
85 return int(input_val)
86
87 @classmethod
88 def _list_func(cls, input_val, sep=','):
89 return aslist(input_val, sep=sep)
90
91 @classmethod
92 def _string_func(cls, input_val, lower=True):
93 if lower:
94 input_val = input_val.lower()
95 return input_val
96
97 @classmethod
98 def _float_func(cls, input_val):
99 return float(input_val)
100
101 @classmethod
102 def _dir_func(cls, input_val, ensure_dir=False, mode=0o755):
103
104 # ensure we have our dir created
105 if not os.path.isdir(input_val) and ensure_dir:
106 os.makedirs(input_val, mode=mode)
107
108 if not os.path.isdir(input_val):
109 raise Exception('Dir at {} does not exist'.format(input_val))
110 return input_val
111
112 @classmethod
113 def _file_path_func(cls, input_val, ensure_dir=False, mode=0o755):
114 dirname = os.path.dirname(input_val)
115 cls._dir_func(dirname, ensure_dir=ensure_dir)
116 return input_val
117
118 @classmethod
119 def _key_transformator(cls, key):
120 return "{}_{}".format('RC'.upper(), key.upper().replace('.', '_').replace('-', '_'))
121
122 def enable_logging(self, logging_conf=None, level='INFO', formatter='generic'):
123 """
124 Helper to enable debug on running instance
125 :return:
126 """
127
128 if not str2bool(self.settings.get('logging.autoconfigure')):
129 log.info('logging configuration based on main .ini file')
130 return
131
132 if logging_conf is None:
133 logging_conf = self.settings.get('logging.logging_conf_file') or ''
134
135 if not os.path.isfile(logging_conf):
136 log.error('Unable to setup logging based on %s, file does not exist...', logging_conf)
137 return
138
139 with open(logging_conf, 'rb') as f:
140 ini_template = textwrap.dedent(f.read())
141 ini_template = string.Template(ini_template).safe_substitute(
142 RC_LOGGING_LEVEL=os.environ.get('RC_LOGGING_LEVEL', '') or level,
143 RC_LOGGING_FORMATTER=os.environ.get('RC_LOGGING_FORMATTER', '') or formatter
144 )
145
146 with tempfile.NamedTemporaryFile(prefix='rc_logging_', suffix='.ini', delete=False) as f:
147 log.info('Saved Temporary LOGGING config at %s', f.name)
148 f.write(ini_template)
149
150 logging.config.fileConfig(f.name)
151 os.remove(f.name)
152
153 def make_setting(self, key, default, lower=False, default_when_empty=False, parser=None):
154
155 input_val = self.settings.get(key, default)
156
157 if default_when_empty and not input_val:
158 # use default value when value is set in the config but it is empty
159 input_val = default
160
161 parser_func = {
162 'bool': self._bool_func,
163 'int': self._int_func,
164 'list': self._list_func,
165 'list:newline': functools.partial(self._list_func, sep='/n'),
166 'list:spacesep': functools.partial(self._list_func, sep=' '),
167 'string': functools.partial(self._string_func, lower=lower),
168 'dir': self._dir_func,
169 'dir:ensured': functools.partial(self._dir_func, ensure_dir=True),
170 'file': self._file_path_func,
171 'file:ensured': functools.partial(self._file_path_func, ensure_dir=True),
172 None: lambda i: i
173 }[parser]
174
175 # now maybe we have this KEY in env, search and use the value with higher priority.
176 transformed_key = self._key_transformator(key)
177 envvar_value = os.environ.get(transformed_key)
178 if envvar_value:
179 log.debug('using `%s` key instead of `%s` key for config', transformed_key, key)
180 input_val = envvar_value
181 self.settings[key] = parser_func(input_val)
182 return self.settings[key]
@@ -1,269 +1,272 b''
1 1 """
2 2 Gunicorn config extension and hooks. This config file adds some extra settings and memory management.
3 3 Gunicorn configuration should be managed by .ini files entries of RhodeCode or VCSServer
4 4 """
5 5
6 6 import gc
7 7 import os
8 8 import sys
9 9 import math
10 10 import time
11 11 import threading
12 12 import traceback
13 13 import random
14 14 from gunicorn.glogging import Logger
15 15
16 16
17 17 def get_workers():
18 18 import multiprocessing
19 19 return multiprocessing.cpu_count() * 2 + 1
20 20
21 21 # GLOBAL
22 22 errorlog = '-'
23 23 accesslog = '-'
24 24
25 25
26 26 # SERVER MECHANICS
27 27 # None == system temp dir
28 28 # worker_tmp_dir is recommended to be set to some tmpfs
29 29 worker_tmp_dir = None
30 30 tmp_upload_dir = None
31 31
32 32 # Custom log format
33 access_log_format = (
34 '%(t)s %(p)s INFO [GNCRN] %(h)-15s rqt:%(L)s %(s)s %(b)-6s "%(m)s:%(U)s %(q)s" usr:%(u)s "%(f)s" "%(a)s"')
33 #access_log_format = (
34 # '%(t)s %(p)s INFO [GNCRN] %(h)-15s rqt:%(L)s %(s)s %(b)-6s "%(m)s:%(U)s %(q)s" usr:%(u)s "%(f)s" "%(a)s"')
35 35
36 36 # loki format for easier parsing in grafana
37 #access_log_format = (
38 # 'time="%(t)s" pid=%(p)s level="INFO" type="[GNCRN]" ip="%(h)-15s" rqt="%(L)s" response_code="%(s)s" response_bytes="%(b)-6s" uri="%(m)s:%(U)s %(q)s" user=":%(u)s" user_agent="%(a)s"')
37 access_log_format = (
38 'time="%(t)s" pid=%(p)s level="INFO" type="[GNCRN]" ip="%(h)-15s" rqt="%(L)s" response_code="%(s)s" response_bytes="%(b)-6s" uri="%(m)s:%(U)s %(q)s" user=":%(u)s" user_agent="%(a)s"')
39 39
40 40 # self adjust workers based on CPU count
41 41 # workers = get_workers()
42 42
43 43
44 44 def _get_process_rss(pid=None):
45 45 try:
46 46 import psutil
47 47 if pid:
48 48 proc = psutil.Process(pid)
49 49 else:
50 50 proc = psutil.Process()
51 51 return proc.memory_info().rss
52 52 except Exception:
53 53 return None
54 54
55 55
56 56 def _get_config(ini_path):
57 57
58 58 try:
59 59 import configparser
60 60 except ImportError:
61 61 import ConfigParser as configparser
62 62 try:
63 63 config = configparser.RawConfigParser()
64 64 config.read(ini_path)
65 65 return config
66 66 except Exception:
67 67 return None
68 68
69 69
70 70 def _time_with_offset(memory_usage_check_interval):
71 71 return time.time() - random.randint(0, memory_usage_check_interval/2.0)
72 72
73 73
74 74 def pre_fork(server, worker):
75 75 pass
76 76
77 77
78 78 def post_fork(server, worker):
79 79
80 80 # memory spec defaults
81 81 _memory_max_usage = 0
82 82 _memory_usage_check_interval = 60
83 83 _memory_usage_recovery_threshold = 0.8
84 84
85 85 ini_path = os.path.abspath(server.cfg.paste)
86 86 conf = _get_config(ini_path)
87 87
88 88 section = 'server:main'
89 89 if conf and conf.has_section(section):
90 90
91 91 if conf.has_option(section, 'memory_max_usage'):
92 92 _memory_max_usage = conf.getint(section, 'memory_max_usage')
93 93
94 94 if conf.has_option(section, 'memory_usage_check_interval'):
95 95 _memory_usage_check_interval = conf.getint(section, 'memory_usage_check_interval')
96 96
97 97 if conf.has_option(section, 'memory_usage_recovery_threshold'):
98 98 _memory_usage_recovery_threshold = conf.getfloat(section, 'memory_usage_recovery_threshold')
99 99
100 worker._memory_max_usage = _memory_max_usage
101 worker._memory_usage_check_interval = _memory_usage_check_interval
102 worker._memory_usage_recovery_threshold = _memory_usage_recovery_threshold
100 worker._memory_max_usage = int(os.environ.get('RC_GUNICORN_MEMORY_MAX_USAGE', '')
101 or _memory_max_usage)
102 worker._memory_usage_check_interval = int(os.environ.get('RC_GUNICORN_MEMORY_USAGE_CHECK_INTERVAL', '')
103 or _memory_usage_check_interval)
104 worker._memory_usage_recovery_threshold = float(os.environ.get('RC_GUNICORN_MEMORY_USAGE_RECOVERY_THRESHOLD', '')
105 or _memory_usage_recovery_threshold)
103 106
104 107 # register memory last check time, with some random offset so we don't recycle all
105 108 # at once
106 109 worker._last_memory_check_time = _time_with_offset(_memory_usage_check_interval)
107 110
108 111 if _memory_max_usage:
109 112 server.log.info("[%-10s] WORKER spawned with max memory set at %s", worker.pid,
110 113 _format_data_size(_memory_max_usage))
111 114 else:
112 115 server.log.info("[%-10s] WORKER spawned", worker.pid)
113 116
114 117
115 118 def pre_exec(server):
116 119 server.log.info("Forked child, re-executing.")
117 120
118 121
119 122 def on_starting(server):
120 123 server_lbl = '{} {}'.format(server.proc_name, server.address)
121 124 server.log.info("Server %s is starting.", server_lbl)
122 125
123 126
124 127 def when_ready(server):
125 128 server.log.info("Server %s is ready. Spawning workers", server)
126 129
127 130
128 131 def on_reload(server):
129 132 pass
130 133
131 134
132 135 def _format_data_size(size, unit="B", precision=1, binary=True):
133 136 """Format a number using SI units (kilo, mega, etc.).
134 137
135 138 ``size``: The number as a float or int.
136 139
137 140 ``unit``: The unit name in plural form. Examples: "bytes", "B".
138 141
139 142 ``precision``: How many digits to the right of the decimal point. Default
140 143 is 1. 0 suppresses the decimal point.
141 144
142 145 ``binary``: If false, use base-10 decimal prefixes (kilo = K = 1000).
143 146 If true, use base-2 binary prefixes (kibi = Ki = 1024).
144 147
145 148 ``full_name``: If false (default), use the prefix abbreviation ("k" or
146 149 "Ki"). If true, use the full prefix ("kilo" or "kibi"). If false,
147 150 use abbreviation ("k" or "Ki").
148 151
149 152 """
150 153
151 154 if not binary:
152 155 base = 1000
153 156 multiples = ('', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
154 157 else:
155 158 base = 1024
156 159 multiples = ('', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi')
157 160
158 161 sign = ""
159 162 if size > 0:
160 163 m = int(math.log(size, base))
161 164 elif size < 0:
162 165 sign = "-"
163 166 size = -size
164 167 m = int(math.log(size, base))
165 168 else:
166 169 m = 0
167 170 if m > 8:
168 171 m = 8
169 172
170 173 if m == 0:
171 174 precision = '%.0f'
172 175 else:
173 176 precision = '%%.%df' % precision
174 177
175 178 size = precision % (size / math.pow(base, m))
176 179
177 180 return '%s%s %s%s' % (sign, size.strip(), multiples[m], unit)
178 181
179 182
180 183 def _check_memory_usage(worker):
181 184 memory_max_usage = worker._memory_max_usage
182 185 if not memory_max_usage:
183 186 return
184 187
185 188 memory_usage_check_interval = worker._memory_usage_check_interval
186 189 memory_usage_recovery_threshold = memory_max_usage * worker._memory_usage_recovery_threshold
187 190
188 191 elapsed = time.time() - worker._last_memory_check_time
189 192 if elapsed > memory_usage_check_interval:
190 193 mem_usage = _get_process_rss()
191 194 if mem_usage and mem_usage > memory_max_usage:
192 195 worker.log.info(
193 196 "memory usage %s > %s, forcing gc",
194 197 _format_data_size(mem_usage), _format_data_size(memory_max_usage))
195 198 # Try to clean it up by forcing a full collection.
196 199 gc.collect()
197 200 mem_usage = _get_process_rss()
198 201 if mem_usage > memory_usage_recovery_threshold:
199 202 # Didn't clean up enough, we'll have to terminate.
200 203 worker.log.warning(
201 204 "memory usage %s > %s after gc, quitting",
202 205 _format_data_size(mem_usage), _format_data_size(memory_max_usage))
203 206 # This will cause worker to auto-restart itself
204 207 worker.alive = False
205 208 worker._last_memory_check_time = time.time()
206 209
207 210
208 211 def worker_int(worker):
209 212 worker.log.info("[%-10s] worker received INT or QUIT signal", worker.pid)
210 213
211 214 # get traceback info, on worker crash
212 215 id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
213 216 code = []
214 217 for thread_id, stack in sys._current_frames().items():
215 218 code.append(
216 219 "\n# Thread: %s(%d)" % (id2name.get(thread_id, ""), thread_id))
217 220 for fname, lineno, name, line in traceback.extract_stack(stack):
218 221 code.append('File: "%s", line %d, in %s' % (fname, lineno, name))
219 222 if line:
220 223 code.append(" %s" % (line.strip()))
221 224 worker.log.debug("\n".join(code))
222 225
223 226
224 227 def worker_abort(worker):
225 228 worker.log.info("[%-10s] worker received SIGABRT signal", worker.pid)
226 229
227 230
228 231 def worker_exit(server, worker):
229 232 worker.log.info("[%-10s] worker exit", worker.pid)
230 233
231 234
232 235 def child_exit(server, worker):
233 236 worker.log.info("[%-10s] worker child exit", worker.pid)
234 237
235 238
236 239 def pre_request(worker, req):
237 240 worker.start_time = time.time()
238 241 worker.log.debug(
239 242 "GNCRN PRE WORKER [cnt:%s]: %s %s", worker.nr, req.method, req.path)
240 243
241 244
242 245 def post_request(worker, req, environ, resp):
243 246 total_time = time.time() - worker.start_time
244 247 # Gunicorn sometimes has problems with reading the status_code
245 248 status_code = getattr(resp, 'status_code', '')
246 249 worker.log.debug(
247 250 "GNCRN POST WORKER [cnt:%s]: %s %s resp: %s, Load Time: %.4fs",
248 251 worker.nr, req.method, req.path, status_code, total_time)
249 252 _check_memory_usage(worker)
250 253
251 254
252 255 class RhodeCodeLogger(Logger):
253 256 """
254 257 Custom Logger that allows some customization that gunicorn doesn't allow
255 258 """
256 259
257 260 datefmt = r"%Y-%m-%d %H:%M:%S"
258 261
259 262 def __init__(self, cfg):
260 263 Logger.__init__(self, cfg)
261 264
262 265 def now(self):
263 266 """ return date in RhodeCode Log format """
264 267 now = time.time()
265 268 msecs = int((now - long(now)) * 1000)
266 269 return time.strftime(self.datefmt, time.localtime(now)) + '.{0:03d}'.format(msecs)
267 270
268 271
269 272 logger_class = RhodeCodeLogger
@@ -1,816 +1,825 b''
1 1 ## -*- coding: utf-8 -*-
2 2
3 3 ; #########################################
4 4 ; RHODECODE COMMUNITY EDITION CONFIGURATION
5 5 ; #########################################
6 6
7 7 [DEFAULT]
8 8 ; Debug flag sets all loggers to debug, and enables request tracking
9 9 debug = false
10 10
11 11 ; ########################################################################
12 12 ; EMAIL CONFIGURATION
13 13 ; These settings will be used by the RhodeCode mailing system
14 14 ; ########################################################################
15 15
16 16 ; prefix all emails subjects with given prefix, helps filtering out emails
17 17 #email_prefix = [RhodeCode]
18 18
19 19 ; email FROM address all mails will be sent
20 20 #app_email_from = rhodecode-noreply@localhost
21 21
22 22 #smtp_server = mail.server.com
23 23 #smtp_username =
24 24 #smtp_password =
25 25 #smtp_port =
26 26 #smtp_use_tls = false
27 27 #smtp_use_ssl = true
28 28
29 29 [server:main]
30 30 ; COMMON HOST/IP CONFIG
31 31 host = 127.0.0.1
32 32 port = 5000
33 33
34 34
35 35 ; ###########################
36 36 ; GUNICORN APPLICATION SERVER
37 37 ; ###########################
38 38
39 39 ; run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
40 40
41 41 ; Module to use, this setting shouldn't be changed
42 42 use = egg:gunicorn#main
43 43
44 44 ; Sets the number of process workers. More workers means more concurrent connections
45 45 ; RhodeCode can handle at the same time. Each additional worker also it increases
46 46 ; memory usage as each has it's own set of caches.
47 47 ; Recommended value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers, but no more
48 48 ; than 8-10 unless for really big deployments .e.g 700-1000 users.
49 49 ; `instance_id = *` must be set in the [app:main] section below (which is the default)
50 50 ; when using more than 1 worker.
51 51 workers = 2
52 52
53 53 ; Gunicorn access log level
54 54 loglevel = info
55 55
56 56 ; Process name visible in process list
57 57 proc_name = rhodecode
58 58
59 59 ; Type of worker class, one of `sync`, `gevent`
60 60 ; Recommended type is `gevent`
61 61 worker_class = gevent
62 62
63 63 ; The maximum number of simultaneous clients per worker. Valid only for gevent
64 64 worker_connections = 10
65 65
66 66 ; Max number of requests that worker will handle before being gracefully restarted.
67 67 ; Prevents memory leaks, jitter adds variability so not all workers are restarted at once.
68 68 max_requests = 1000
69 69 max_requests_jitter = 30
70 70
71 71 ; Amount of time a worker can spend with handling a request before it
72 72 ; gets killed and restarted. By default set to 21600 (6hrs)
73 73 ; Examples: 1800 (30min), 3600 (1hr), 7200 (2hr), 43200 (12h)
74 74 timeout = 21600
75 75
76 76 ; The maximum size of HTTP request line in bytes.
77 77 ; 0 for unlimited
78 78 limit_request_line = 0
79 79
80 80 ; Limit the number of HTTP headers fields in a request.
81 81 ; By default this value is 100 and can't be larger than 32768.
82 82 limit_request_fields = 32768
83 83
84 84 ; Limit the allowed size of an HTTP request header field.
85 85 ; Value is a positive number or 0.
86 86 ; Setting it to 0 will allow unlimited header field sizes.
87 87 limit_request_field_size = 0
88 88
89 89 ; Timeout for graceful workers restart.
90 90 ; After receiving a restart signal, workers have this much time to finish
91 91 ; serving requests. Workers still alive after the timeout (starting from the
92 92 ; receipt of the restart signal) are force killed.
93 93 ; Examples: 1800 (30min), 3600 (1hr), 7200 (2hr), 43200 (12h)
94 94 graceful_timeout = 3600
95 95
96 96 # The number of seconds to wait for requests on a Keep-Alive connection.
97 97 # Generally set in the 1-5 seconds range.
98 98 keepalive = 2
99 99
100 100 ; Maximum memory usage that each worker can use before it will receive a
101 101 ; graceful restart signal 0 = memory monitoring is disabled
102 102 ; Examples: 268435456 (256MB), 536870912 (512MB)
103 103 ; 1073741824 (1GB), 2147483648 (2GB), 4294967296 (4GB)
104 104 memory_max_usage = 0
105 105
106 106 ; How often in seconds to check for memory usage for each gunicorn worker
107 107 memory_usage_check_interval = 60
108 108
109 109 ; Threshold value for which we don't recycle worker if GarbageCollection
110 110 ; frees up enough resources. Before each restart we try to run GC on worker
111 111 ; in case we get enough free memory after that, restart will not happen.
112 112 memory_usage_recovery_threshold = 0.8
113 113
114 114
115 115 ; Prefix middleware for RhodeCode.
116 116 ; recommended when using proxy setup.
117 117 ; allows to set RhodeCode under a prefix in server.
118 118 ; eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
119 119 ; And set your prefix like: `prefix = /custom_prefix`
120 120 ; be sure to also set beaker.session.cookie_path = /custom_prefix if you need
121 121 ; to make your cookies only work on prefix url
122 122 [filter:proxy-prefix]
123 123 use = egg:PasteDeploy#prefix
124 124 prefix = /
125 125
126 126 [app:main]
127 ; The %(here)s variable will be replaced with the absolute path of parent directory
128 ; of this file
129 ; In addition ENVIRONMENT variables usage is possible, e.g
130 ; sqlalchemy.db1.url = {ENV_RC_DB_URL}
131 127
132 128 use = egg:rhodecode-enterprise-ce
133 129
134 130 ; enable proxy prefix middleware, defined above
135 131 #filter-with = proxy-prefix
136 132
137 133 ; encryption key used to encrypt social plugin tokens,
138 134 ; remote_urls with credentials etc, if not set it defaults to
139 135 ; `beaker.session.secret`
140 136 #rhodecode.encrypted_values.secret =
141 137
142 138 ; decryption strict mode (enabled by default). It controls if decryption raises
143 139 ; `SignatureVerificationError` in case of wrong key, or damaged encryption data.
144 140 #rhodecode.encrypted_values.strict = false
145 141
146 142 ; Pick algorithm for encryption. Either fernet (more secure) or aes (default)
147 143 ; fernet is safer, and we strongly recommend switching to it.
148 144 ; Due to backward compatibility aes is used as default.
149 145 #rhodecode.encrypted_values.algorithm = fernet
150 146
151 147 ; Return gzipped responses from RhodeCode (static files/application)
152 148 gzip_responses = false
153 149
154 150 ; Auto-generate javascript routes file on startup
155 151 generate_js_files = false
156 152
157 153 ; System global default language.
158 154 ; All available languages: en (default), be, de, es, fr, it, ja, pl, pt, ru, zh
159 155 lang = en
160 156
161 157 ; Perform a full repository scan and import on each server start.
162 158 ; Settings this to true could lead to very long startup time.
163 159 startup.import_repos = false
164 160
165 161 ; Uncomment and set this path to use archive download cache.
166 162 ; Once enabled, generated archives will be cached at this location
167 163 ; and served from the cache during subsequent requests for the same archive of
168 164 ; the repository.
169 165 #archive_cache_dir = /tmp/tarballcache
170 166
171 167 ; URL at which the application is running. This is used for Bootstrapping
172 168 ; requests in context when no web request is available. Used in ishell, or
173 169 ; SSH calls. Set this for events to receive proper url for SSH calls.
174 170 app.base_url = http://rhodecode.local
175 171
176 172 ; Unique application ID. Should be a random unique string for security.
177 173 app_instance_uuid = rc-production
178 174
179 175 ; Cut off limit for large diffs (size in bytes). If overall diff size on
180 176 ; commit, or pull request exceeds this limit this diff will be displayed
181 177 ; partially. E.g 512000 == 512Kb
182 178 cut_off_limit_diff = 512000
183 179
184 180 ; Cut off limit for large files inside diffs (size in bytes). Each individual
185 181 ; file inside diff which exceeds this limit will be displayed partially.
186 182 ; E.g 128000 == 128Kb
187 183 cut_off_limit_file = 128000
188 184
189 185 ; Use cached version of vcs repositories everywhere. Recommended to be `true`
190 186 vcs_full_cache = true
191 187
192 188 ; Force https in RhodeCode, fixes https redirects, assumes it's always https.
193 189 ; Normally this is controlled by proper flags sent from http server such as Nginx or Apache
194 190 force_https = false
195 191
196 192 ; use Strict-Transport-Security headers
197 193 use_htsts = false
198 194
199 195 ; Set to true if your repos are exposed using the dumb protocol
200 196 git_update_server_info = false
201 197
202 198 ; RSS/ATOM feed options
203 199 rss_cut_off_limit = 256000
204 200 rss_items_per_page = 10
205 201 rss_include_diff = false
206 202
207 203 ; gist URL alias, used to create nicer urls for gist. This should be an
208 204 ; url that does rewrites to _admin/gists/{gistid}.
209 205 ; example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
210 206 ; RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
211 207 gist_alias_url =
212 208
213 209 ; List of views (using glob pattern syntax) that AUTH TOKENS could be
214 210 ; used for access.
215 211 ; Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
216 212 ; came from the the logged in user who own this authentication token.
217 213 ; Additionally @TOKEN syntax can be used to bound the view to specific
218 214 ; authentication token. Such view would be only accessible when used together
219 215 ; with this authentication token
220 216 ; list of all views can be found under `/_admin/permissions/auth_token_access`
221 217 ; The list should be "," separated and on a single line.
222 218 ; Most common views to enable:
223 219
224 220 # RepoCommitsView:repo_commit_download
225 221 # RepoCommitsView:repo_commit_patch
226 222 # RepoCommitsView:repo_commit_raw
227 223 # RepoCommitsView:repo_commit_raw@TOKEN
228 224 # RepoFilesView:repo_files_diff
229 225 # RepoFilesView:repo_archivefile
230 226 # RepoFilesView:repo_file_raw
231 227 # GistView:*
232 228 api_access_controllers_whitelist =
233 229
234 230 ; Default encoding used to convert from and to unicode
235 231 ; can be also a comma separated list of encoding in case of mixed encodings
236 232 default_encoding = UTF-8
237 233
238 234 ; instance-id prefix
239 235 ; a prefix key for this instance used for cache invalidation when running
240 236 ; multiple instances of RhodeCode, make sure it's globally unique for
241 237 ; all running RhodeCode instances. Leave empty if you don't use it
242 238 instance_id =
243 239
244 240 ; Fallback authentication plugin. Set this to a plugin ID to force the usage
245 241 ; of an authentication plugin also if it is disabled by it's settings.
246 242 ; This could be useful if you are unable to log in to the system due to broken
247 243 ; authentication settings. Then you can enable e.g. the internal RhodeCode auth
248 244 ; module to log in again and fix the settings.
249 245 ; Available builtin plugin IDs (hash is part of the ID):
250 246 ; egg:rhodecode-enterprise-ce#rhodecode
251 247 ; egg:rhodecode-enterprise-ce#pam
252 248 ; egg:rhodecode-enterprise-ce#ldap
253 249 ; egg:rhodecode-enterprise-ce#jasig_cas
254 250 ; egg:rhodecode-enterprise-ce#headers
255 251 ; egg:rhodecode-enterprise-ce#crowd
256 252
257 253 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
258 254
259 255 ; Flag to control loading of legacy plugins in py:/path format
260 256 auth_plugin.import_legacy_plugins = true
261 257
262 258 ; alternative return HTTP header for failed authentication. Default HTTP
263 259 ; response is 401 HTTPUnauthorized. Currently HG clients have troubles with
264 260 ; handling that causing a series of failed authentication calls.
265 261 ; Set this variable to 403 to return HTTPForbidden, or any other HTTP code
266 262 ; This will be served instead of default 401 on bad authentication
267 263 auth_ret_code =
268 264
269 265 ; use special detection method when serving auth_ret_code, instead of serving
270 266 ; ret_code directly, use 401 initially (Which triggers credentials prompt)
271 267 ; and then serve auth_ret_code to clients
272 268 auth_ret_code_detection = false
273 269
274 270 ; locking return code. When repository is locked return this HTTP code. 2XX
275 271 ; codes don't break the transactions while 4XX codes do
276 272 lock_ret_code = 423
277 273
278 274 ; allows to change the repository location in settings page
279 275 allow_repo_location_change = true
280 276
281 277 ; allows to setup custom hooks in settings page
282 278 allow_custom_hooks_settings = true
283 279
284 280 ; Generated license token required for EE edition license.
285 281 ; New generated token value can be found in Admin > settings > license page.
286 282 license_token =
287 283
288 284 ; This flag hides sensitive information on the license page such as token, and license data
289 285 license.hide_license_info = false
290 286
291 287 ; supervisor connection uri, for managing supervisor and logs.
292 288 supervisor.uri =
293 289
294 290 ; supervisord group name/id we only want this RC instance to handle
295 291 supervisor.group_id = prod
296 292
297 293 ; Display extended labs settings
298 294 labs_settings_active = true
299 295
300 296 ; Custom exception store path, defaults to TMPDIR
301 297 ; This is used to store exception from RhodeCode in shared directory
302 298 #exception_tracker.store_path =
303 299
304 300 ; Send email with exception details when it happens
305 301 #exception_tracker.send_email = false
306 302
307 303 ; Comma separated list of recipients for exception emails,
308 304 ; e.g admin@rhodecode.com,devops@rhodecode.com
309 305 ; Can be left empty, then emails will be sent to ALL super-admins
310 306 #exception_tracker.send_email_recipients =
311 307
312 308 ; optional prefix to Add to email Subject
313 309 #exception_tracker.email_prefix = [RHODECODE ERROR]
314 310
315 311 ; File store configuration. This is used to store and serve uploaded files
316 312 file_store.enabled = true
317 313
318 314 ; Storage backend, available options are: local
319 315 file_store.backend = local
320 316
321 317 ; path to store the uploaded binaries
322 318 file_store.storage_path = %(here)s/data/file_store
323 319
324 320
325 321 ; #############
326 322 ; CELERY CONFIG
327 323 ; #############
328 324
329 325 ; manually run celery: /path/to/celery worker -E --beat --app rhodecode.lib.celerylib.loader --scheduler rhodecode.lib.celerylib.scheduler.RcScheduler --loglevel DEBUG --ini /path/to/rhodecode.ini
330 326
331 327 use_celery = false
332 328
329 ; path to store schedule database
330 #celerybeat-schedule.path =
331
333 332 ; connection url to the message broker (default redis)
334 333 celery.broker_url = redis://localhost:6379/8
335 334
336 335 ; rabbitmq example
337 336 #celery.broker_url = amqp://rabbitmq:qweqwe@localhost:5672/rabbitmqhost
338 337
339 338 ; maximum tasks to execute before worker restart
340 339 celery.max_tasks_per_child = 100
341 340
342 341 ; tasks will never be sent to the queue, but executed locally instead.
343 342 celery.task_always_eager = false
344 343
345 344 ; #############
346 345 ; DOGPILE CACHE
347 346 ; #############
348 347
349 348 ; Default cache dir for caches. Putting this into a ramdisk can boost performance.
350 349 ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space
351 350 cache_dir = %(here)s/data
352 351
353 352 ; *********************************************
354 353 ; `sql_cache_short` cache for heavy SQL queries
355 354 ; Only supported backend is `memory_lru`
356 355 ; *********************************************
357 356 rc_cache.sql_cache_short.backend = dogpile.cache.rc.memory_lru
358 357 rc_cache.sql_cache_short.expiration_time = 30
359 358
360 359
361 360 ; *****************************************************
362 361 ; `cache_repo_longterm` cache for repo object instances
363 362 ; Only supported backend is `memory_lru`
364 363 ; *****************************************************
365 364 rc_cache.cache_repo_longterm.backend = dogpile.cache.rc.memory_lru
366 365 ; by default we use 30 Days, cache is still invalidated on push
367 366 rc_cache.cache_repo_longterm.expiration_time = 2592000
368 367 ; max items in LRU cache, set to smaller number to save memory, and expire last used caches
369 368 rc_cache.cache_repo_longterm.max_size = 10000
370 369
371 370
372 371 ; *************************************************
373 372 ; `cache_perms` cache for permission tree, auth TTL
374 373 ; *************************************************
375 374 rc_cache.cache_perms.backend = dogpile.cache.rc.file_namespace
376 375 rc_cache.cache_perms.expiration_time = 300
377 376 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
378 377 #rc_cache.cache_perms.arguments.filename = /tmp/cache_perms.db
379 378
380 379 ; alternative `cache_perms` redis backend with distributed lock
381 380 #rc_cache.cache_perms.backend = dogpile.cache.rc.redis
382 381 #rc_cache.cache_perms.expiration_time = 300
383 382
384 383 ; redis_expiration_time needs to be greater then expiration_time
385 384 #rc_cache.cache_perms.arguments.redis_expiration_time = 7200
386 385
387 386 #rc_cache.cache_perms.arguments.host = localhost
388 387 #rc_cache.cache_perms.arguments.port = 6379
389 388 #rc_cache.cache_perms.arguments.db = 0
390 389 #rc_cache.cache_perms.arguments.socket_timeout = 30
391 390 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
392 391 #rc_cache.cache_perms.arguments.distributed_lock = true
393 392
394 393 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
395 394 #rc_cache.cache_perms.arguments.lock_auto_renewal = true
396 395
397 396 ; ***************************************************
398 397 ; `cache_repo` cache for file tree, Readme, RSS FEEDS
399 398 ; ***************************************************
400 399 rc_cache.cache_repo.backend = dogpile.cache.rc.file_namespace
401 400 rc_cache.cache_repo.expiration_time = 2592000
402 401 ; file cache store path. Defaults to `cache_dir =` value or tempdir if both values are not set
403 402 #rc_cache.cache_repo.arguments.filename = /tmp/cache_repo.db
404 403
405 404 ; alternative `cache_repo` redis backend with distributed lock
406 405 #rc_cache.cache_repo.backend = dogpile.cache.rc.redis
407 406 #rc_cache.cache_repo.expiration_time = 2592000
408 407
409 408 ; redis_expiration_time needs to be greater then expiration_time
410 409 #rc_cache.cache_repo.arguments.redis_expiration_time = 2678400
411 410
412 411 #rc_cache.cache_repo.arguments.host = localhost
413 412 #rc_cache.cache_repo.arguments.port = 6379
414 413 #rc_cache.cache_repo.arguments.db = 1
415 414 #rc_cache.cache_repo.arguments.socket_timeout = 30
416 415 ; more Redis options: https://dogpilecache.sqlalchemy.org/en/latest/api.html#redis-backends
417 416 #rc_cache.cache_repo.arguments.distributed_lock = true
418 417
419 418 ; auto-renew lock to prevent stale locks, slower but safer. Use only if problems happen
420 419 #rc_cache.cache_repo.arguments.lock_auto_renewal = true
421 420
422 421 ; ##############
423 422 ; BEAKER SESSION
424 423 ; ##############
425 424
426 425 ; beaker.session.type is type of storage options for the logged users sessions. Current allowed
427 426 ; types are file, ext:redis, ext:database, ext:memcached, and memory (default if not specified).
428 427 ; Fastest ones are Redis and ext:database
429 428 beaker.session.type = file
430 429 beaker.session.data_dir = %(here)s/data/sessions
431 430
432 431 ; Redis based sessions
433 432 #beaker.session.type = ext:redis
434 433 #beaker.session.url = redis://127.0.0.1:6379/2
435 434
436 435 ; DB based session, fast, and allows easy management over logged in users
437 436 #beaker.session.type = ext:database
438 437 #beaker.session.table_name = db_session
439 438 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
440 439 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
441 440 #beaker.session.sa.pool_recycle = 3600
442 441 #beaker.session.sa.echo = false
443 442
444 443 beaker.session.key = rhodecode
445 444 beaker.session.secret = production-rc-uytcxaz
446 445 beaker.session.lock_dir = %(here)s/data/sessions/lock
447 446
448 447 ; Secure encrypted cookie. Requires AES and AES python libraries
449 448 ; you must disable beaker.session.secret to use this
450 449 #beaker.session.encrypt_key = key_for_encryption
451 450 #beaker.session.validate_key = validation_key
452 451
453 452 ; Sets session as invalid (also logging out user) if it haven not been
454 453 ; accessed for given amount of time in seconds
455 454 beaker.session.timeout = 2592000
456 455 beaker.session.httponly = true
457 456
458 457 ; Path to use for the cookie. Set to prefix if you use prefix middleware
459 458 #beaker.session.cookie_path = /custom_prefix
460 459
461 460 ; Set https secure cookie
462 461 beaker.session.secure = false
463 462
464 463 ; default cookie expiration time in seconds, set to `true` to set expire
465 464 ; at browser close
466 465 #beaker.session.cookie_expires = 3600
467 466
468 467 ; #############################
469 468 ; SEARCH INDEXING CONFIGURATION
470 469 ; #############################
471 470
472 471 ; Full text search indexer is available in rhodecode-tools under
473 472 ; `rhodecode-tools index` command
474 473
475 474 ; WHOOSH Backend, doesn't require additional services to run
476 475 ; it works good with few dozen repos
477 476 search.module = rhodecode.lib.index.whoosh
478 477 search.location = %(here)s/data/index
479 478
480 479 ; ####################
481 480 ; CHANNELSTREAM CONFIG
482 481 ; ####################
483 482
484 483 ; channelstream enables persistent connections and live notification
485 484 ; in the system. It's also used by the chat system
486 485
487 486 channelstream.enabled = false
488 487
489 488 ; server address for channelstream server on the backend
490 489 channelstream.server = 127.0.0.1:9800
491 490
492 491 ; location of the channelstream server from outside world
493 492 ; use ws:// for http or wss:// for https. This address needs to be handled
494 493 ; by external HTTP server such as Nginx or Apache
495 494 ; see Nginx/Apache configuration examples in our docs
496 495 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
497 496 channelstream.secret = secret
498 497 channelstream.history.location = %(here)s/channelstream_history
499 498
500 499 ; Internal application path that Javascript uses to connect into.
501 500 ; If you use proxy-prefix the prefix should be added before /_channelstream
502 501 channelstream.proxy_path = /_channelstream
503 502
504 503
505 504 ; ##############################
506 505 ; MAIN RHODECODE DATABASE CONFIG
507 506 ; ##############################
508 507
509 508 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
510 509 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
511 510 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode?charset=utf8
512 511 ; pymysql is an alternative driver for MySQL, use in case of problems with default one
513 512 #sqlalchemy.db1.url = mysql+pymysql://root:qweqwe@localhost/rhodecode
514 513
515 514 sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
516 515
517 516 ; see sqlalchemy docs for other advanced settings
518 517 ; print the sql statements to output
519 518 sqlalchemy.db1.echo = false
520 519
521 520 ; recycle the connections after this amount of seconds
522 521 sqlalchemy.db1.pool_recycle = 3600
523 522 sqlalchemy.db1.convert_unicode = true
524 523
525 524 ; the number of connections to keep open inside the connection pool.
526 525 ; 0 indicates no limit
527 526 #sqlalchemy.db1.pool_size = 5
528 527
529 528 ; The number of connections to allow in connection pool "overflow", that is
530 529 ; connections that can be opened above and beyond the pool_size setting,
531 530 ; which defaults to five.
532 531 #sqlalchemy.db1.max_overflow = 10
533 532
534 533 ; Connection check ping, used to detect broken database connections
535 534 ; could be enabled to better handle cases if MySQL has gone away errors
536 535 #sqlalchemy.db1.ping_connection = true
537 536
538 537 ; ##########
539 538 ; VCS CONFIG
540 539 ; ##########
541 540 vcs.server.enable = true
542 541 vcs.server = localhost:9900
543 542
544 543 ; Web server connectivity protocol, responsible for web based VCS operations
545 544 ; Available protocols are:
546 545 ; `http` - use http-rpc backend (default)
547 546 vcs.server.protocol = http
548 547
549 548 ; Push/Pull operations protocol, available options are:
550 549 ; `http` - use http-rpc backend (default)
551 550 vcs.scm_app_implementation = http
552 551
553 552 ; Push/Pull operations hooks protocol, available options are:
554 553 ; `http` - use http-rpc backend (default)
555 554 vcs.hooks.protocol = http
556 555
557 556 ; Host on which this instance is listening for hooks. If vcsserver is in other location
558 557 ; this should be adjusted.
559 558 vcs.hooks.host = 127.0.0.1
560 559
561 560 ; Start VCSServer with this instance as a subprocess, useful for development
562 561 vcs.start_server = false
563 562
564 563 ; List of enabled VCS backends, available options are:
565 564 ; `hg` - mercurial
566 565 ; `git` - git
567 566 ; `svn` - subversion
568 567 vcs.backends = hg, git, svn
569 568
570 569 ; Wait this number of seconds before killing connection to the vcsserver
571 570 vcs.connection_timeout = 3600
572 571
573 572 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
574 573 ; Set a numeric version for your current SVN e.g 1.8, or 1.12
575 574 ; Legacy available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
576 575 #vcs.svn.compatible_version = 1.8
577 576
577 ; Cache flag to cache vcsserver remote calls locally
578 ; It uses cache_region `cache_repo`
579 vcs.methods.cache = true
578 580
579 581 ; ####################################################
580 582 ; Subversion proxy support (mod_dav_svn)
581 583 ; Maps RhodeCode repo groups into SVN paths for Apache
582 584 ; ####################################################
583 585
584 586 ; Enable or disable the config file generation.
585 587 svn.proxy.generate_config = false
586 588
587 589 ; Generate config file with `SVNListParentPath` set to `On`.
588 590 svn.proxy.list_parent_path = true
589 591
590 592 ; Set location and file name of generated config file.
591 593 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
592 594
593 595 ; alternative mod_dav config template. This needs to be a valid mako template
594 596 ; Example template can be found in the source code:
595 597 ; rhodecode/apps/svn_support/templates/mod-dav-svn.conf.mako
596 598 #svn.proxy.config_template = ~/.rccontrol/enterprise-1/custom_svn_conf.mako
597 599
598 600 ; Used as a prefix to the `Location` block in the generated config file.
599 601 ; In most cases it should be set to `/`.
600 602 svn.proxy.location_root = /
601 603
602 604 ; Command to reload the mod dav svn configuration on change.
603 605 ; Example: `/etc/init.d/apache2 reload` or /home/USER/apache_reload.sh
604 606 ; Make sure user who runs RhodeCode process is allowed to reload Apache
605 607 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
606 608
607 609 ; If the timeout expires before the reload command finishes, the command will
608 610 ; be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
609 611 #svn.proxy.reload_timeout = 10
610 612
611 613 ; ####################
612 614 ; SSH Support Settings
613 615 ; ####################
614 616
615 617 ; Defines if a custom authorized_keys file should be created and written on
616 618 ; any change user ssh keys. Setting this to false also disables possibility
617 619 ; of adding SSH keys by users from web interface. Super admins can still
618 620 ; manage SSH Keys.
619 621 ssh.generate_authorized_keyfile = false
620 622
621 623 ; Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
622 624 # ssh.authorized_keys_ssh_opts =
623 625
624 626 ; Path to the authorized_keys file where the generate entries are placed.
625 627 ; It is possible to have multiple key files specified in `sshd_config` e.g.
626 628 ; AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
627 629 ssh.authorized_keys_file_path = ~/.ssh/authorized_keys_rhodecode
628 630
629 631 ; Command to execute the SSH wrapper. The binary is available in the
630 632 ; RhodeCode installation directory.
631 633 ; e.g ~/.rccontrol/community-1/profile/bin/rc-ssh-wrapper
632 634 ssh.wrapper_cmd = ~/.rccontrol/community-1/rc-ssh-wrapper
633 635
634 636 ; Allow shell when executing the ssh-wrapper command
635 637 ssh.wrapper_cmd_allow_shell = false
636 638
637 639 ; Enables logging, and detailed output send back to the client during SSH
638 640 ; operations. Useful for debugging, shouldn't be used in production.
639 641 ssh.enable_debug_logging = false
640 642
641 643 ; Paths to binary executable, by default they are the names, but we can
642 644 ; override them if we want to use a custom one
643 645 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
644 646 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
645 647 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
646 648
647 649 ; Enables SSH key generator web interface. Disabling this still allows users
648 650 ; to add their own keys.
649 651 ssh.enable_ui_key_generator = true
650 652
651 653
652 654 ; #################
653 655 ; APPENLIGHT CONFIG
654 656 ; #################
655 657
656 658 ; Appenlight is tailored to work with RhodeCode, see
657 659 ; http://appenlight.rhodecode.com for details how to obtain an account
658 660
659 661 ; Appenlight integration enabled
660 appenlight = false
662 #appenlight = false
661 663
662 appenlight.server_url = https://api.appenlight.com
663 appenlight.api_key = YOUR_API_KEY
664 #appenlight.server_url = https://api.appenlight.com
665 #appenlight.api_key = YOUR_API_KEY
664 666 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
665 667
666 668 ; used for JS client
667 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
669 #appenlight.api_public_key = YOUR_API_PUBLIC_KEY
668 670
669 671 ; TWEAK AMOUNT OF INFO SENT HERE
670 672
671 673 ; enables 404 error logging (default False)
672 appenlight.report_404 = false
674 #appenlight.report_404 = false
673 675
674 676 ; time in seconds after request is considered being slow (default 1)
675 appenlight.slow_request_time = 1
677 #appenlight.slow_request_time = 1
676 678
677 679 ; record slow requests in application
678 680 ; (needs to be enabled for slow datastore recording and time tracking)
679 appenlight.slow_requests = true
681 #appenlight.slow_requests = true
680 682
681 683 ; enable hooking to application loggers
682 appenlight.logging = true
684 #appenlight.logging = true
683 685
684 686 ; minimum log level for log capture
685 appenlight.logging.level = WARNING
687 #ppenlight.logging.level = WARNING
686 688
687 689 ; send logs only from erroneous/slow requests
688 690 ; (saves API quota for intensive logging)
689 appenlight.logging_on_error = false
691 #appenlight.logging_on_error = false
690 692
691 693 ; list of additional keywords that should be grabbed from environ object
692 694 ; can be string with comma separated list of words in lowercase
693 695 ; (by default client will always send following info:
694 696 ; 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
695 697 ; start with HTTP* this list be extended with additional keywords here
696 appenlight.environ_keys_whitelist =
698 #appenlight.environ_keys_whitelist =
697 699
698 700 ; list of keywords that should be blanked from request object
699 701 ; can be string with comma separated list of words in lowercase
700 702 ; (by default client will always blank keys that contain following words
701 703 ; 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
702 704 ; this list be extended with additional keywords set here
703 appenlight.request_keys_blacklist =
705 #appenlight.request_keys_blacklist =
704 706
705 707 ; list of namespaces that should be ignores when gathering log entries
706 708 ; can be string with comma separated list of namespaces
707 709 ; (by default the client ignores own entries: appenlight_client.client)
708 appenlight.log_namespace_blacklist =
710 #appenlight.log_namespace_blacklist =
709 711
710 712 ; Statsd client config, this is used to send metrics to statsd
711 713 ; We recommend setting statsd_exported and scrape them using Promethues
712 714 #statsd.enabled = false
713 715 #statsd.statsd_host = 0.0.0.0
714 716 #statsd.statsd_port = 8125
715 717 #statsd.statsd_prefix =
716 718 #statsd.statsd_ipv6 = false
717 719
718 720
721 ; configure logging automatically at server startup set to false
722 ; to use the below custom logging config.
723 #logging.autoconfigure = true
724
725 ; specify your own custom logging config file to configure logging
726 #logging.logging_conf_file = /path/to/custom_logging.ini
727
719 728 ; Dummy marker to add new entries after.
720 729 ; Add any custom entries below. Please don't remove this marker.
721 730 custom.conf = 1
722 731
723 732
724 733 ; #####################
725 734 ; LOGGING CONFIGURATION
726 735 ; #####################
727 736 [loggers]
728 737 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
729 738
730 739 [handlers]
731 740 keys = console, console_sql
732 741
733 742 [formatters]
734 743 keys = generic, color_formatter, color_formatter_sql
735 744
736 745 ; #######
737 746 ; LOGGERS
738 747 ; #######
739 748 [logger_root]
740 749 level = NOTSET
741 750 handlers = console
742 751
743 752 [logger_sqlalchemy]
744 753 level = INFO
745 754 handlers = console_sql
746 755 qualname = sqlalchemy.engine
747 756 propagate = 0
748 757
749 758 [logger_beaker]
750 759 level = DEBUG
751 760 handlers =
752 761 qualname = beaker.container
753 762 propagate = 1
754 763
755 764 [logger_rhodecode]
756 765 level = DEBUG
757 766 handlers =
758 767 qualname = rhodecode
759 768 propagate = 1
760 769
761 770 [logger_ssh_wrapper]
762 771 level = DEBUG
763 772 handlers =
764 773 qualname = ssh_wrapper
765 774 propagate = 1
766 775
767 776 [logger_celery]
768 777 level = DEBUG
769 778 handlers =
770 779 qualname = celery
771 780
772 781
773 782 ; ########
774 783 ; HANDLERS
775 784 ; ########
776 785
777 786 [handler_console]
778 787 class = StreamHandler
779 788 args = (sys.stderr, )
780 789 level = INFO
781 790 formatter = generic
782 791 ; To enable JSON formatted logs replace generic with json
783 792 ; This allows sending properly formatted logs to grafana loki or elasticsearch
784 793 #formatter = json
785 794
786 795 [handler_console_sql]
787 796 ; "level = DEBUG" logs SQL queries and results.
788 797 ; "level = INFO" logs SQL queries.
789 798 ; "level = WARN" logs neither. (Recommended for production systems.)
790 799 class = StreamHandler
791 800 args = (sys.stderr, )
792 801 level = WARN
793 802 formatter = generic
794 803
795 804 ; ##########
796 805 ; FORMATTERS
797 806 ; ##########
798 807
799 808 [formatter_generic]
800 809 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
801 810 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
802 811 datefmt = %Y-%m-%d %H:%M:%S
803 812
804 813 [formatter_color_formatter]
805 814 class = rhodecode.lib.logging_formatter.ColorFormatter
806 815 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
807 816 datefmt = %Y-%m-%d %H:%M:%S
808 817
809 818 [formatter_color_formatter_sql]
810 819 class = rhodecode.lib.logging_formatter.ColorFormatterSql
811 820 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
812 821 datefmt = %Y-%m-%d %H:%M:%S
813 822
814 823 [formatter_json]
815 824 format = %(message)s
816 825 class = rhodecode.lib._vendor.jsonlogger.JsonFormatter No newline at end of file
@@ -1,65 +1,66 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20 import os
21 21 from rhodecode.apps.file_store import config_keys
22 from rhodecode.config.middleware import _bool_setting, _string_setting
22 from rhodecode.config.settings_maker import SettingsMaker
23 23
24 24
25 25 def _sanitize_settings_and_apply_defaults(settings):
26 26 """
27 27 Set defaults, convert to python types and validate settings.
28 28 """
29 _bool_setting(settings, config_keys.enabled, 'true')
29 settings_maker = SettingsMaker(settings)
30 30
31 _string_setting(settings, config_keys.backend, 'local')
31 settings_maker.make_setting(config_keys.enabled, True, parser='bool')
32 settings_maker.make_setting(config_keys.backend, 'local')
32 33
33 34 default_store = os.path.join(os.path.dirname(settings['__file__']), 'upload_store')
34 _string_setting(settings, config_keys.store_path, default_store)
35 settings_maker.make_setting(config_keys.store_path, default_store)
35 36
36 37
37 38 def includeme(config):
38 39 from rhodecode.apps.file_store.views import FileStoreView
39 40
40 41 settings = config.registry.settings
41 42 _sanitize_settings_and_apply_defaults(settings)
42 43
43 44 config.add_route(
44 45 name='upload_file',
45 46 pattern='/_file_store/upload')
46 47 config.add_view(
47 48 FileStoreView,
48 49 attr='upload_file',
49 50 route_name='upload_file', request_method='POST', renderer='json_ext')
50 51
51 52 config.add_route(
52 53 name='download_file',
53 54 pattern='/_file_store/download/{fid:.*}')
54 55 config.add_view(
55 56 FileStoreView,
56 57 attr='download_file',
57 58 route_name='download_file')
58 59
59 60 config.add_route(
60 61 name='download_file_by_token',
61 62 pattern='/_file_store/token-download/{_auth_token}/{fid:.*}')
62 63 config.add_view(
63 64 FileStoreView,
64 65 attr='download_file_by_token',
65 66 route_name='download_file_by_token')
@@ -1,67 +1,59 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 from . import config_keys
24 24 from .events import SshKeyFileChangeEvent
25 25 from .subscribers import generate_ssh_authorized_keys_file_subscriber
26 26
27 from rhodecode.config.middleware import _bool_setting, _string_setting
27 from rhodecode.config.settings_maker import SettingsMaker
28 28
29 29 log = logging.getLogger(__name__)
30 30
31 31
32 32 def _sanitize_settings_and_apply_defaults(settings):
33 33 """
34 34 Set defaults, convert to python types and validate settings.
35 35 """
36 _bool_setting(settings, config_keys.generate_authorized_keyfile, 'false')
37 _bool_setting(settings, config_keys.wrapper_allow_shell, 'false')
38 _bool_setting(settings, config_keys.enable_debug_logging, 'false')
39 _bool_setting(settings, config_keys.ssh_key_generator_enabled, 'true')
36 settings_maker = SettingsMaker(settings)
37
38 settings_maker.make_setting(config_keys.generate_authorized_keyfile, False, parser='bool')
39 settings_maker.make_setting(config_keys.wrapper_allow_shell, False, parser='bool')
40 settings_maker.make_setting(config_keys.enable_debug_logging, False, parser='bool')
41 settings_maker.make_setting(config_keys.ssh_key_generator_enabled, True, parser='bool')
40 42
41 _string_setting(settings, config_keys.authorized_keys_file_path,
42 '~/.ssh/authorized_keys_rhodecode',
43 lower=False)
44 _string_setting(settings, config_keys.wrapper_cmd, '',
45 lower=False)
46 _string_setting(settings, config_keys.authorized_keys_line_ssh_opts, '',
47 lower=False)
43 settings_maker.make_setting(config_keys.authorized_keys_file_path, '~/.ssh/authorized_keys_rhodecode')
44 settings_maker.make_setting(config_keys.wrapper_cmd, '')
45 settings_maker.make_setting(config_keys.authorized_keys_line_ssh_opts, '')
48 46
49 _string_setting(settings, config_keys.ssh_hg_bin,
50 '~/.rccontrol/vcsserver-1/profile/bin/hg',
51 lower=False)
52 _string_setting(settings, config_keys.ssh_git_bin,
53 '~/.rccontrol/vcsserver-1/profile/bin/git',
54 lower=False)
55 _string_setting(settings, config_keys.ssh_svn_bin,
56 '~/.rccontrol/vcsserver-1/profile/bin/svnserve',
57 lower=False)
47 settings_maker.make_setting(config_keys.ssh_hg_bin, '~/.rccontrol/vcsserver-1/profile/bin/hg')
48 settings_maker.make_setting(config_keys.ssh_git_bin, '~/.rccontrol/vcsserver-1/profile/bin/git')
49 settings_maker.make_setting(config_keys.ssh_svn_bin, '~/.rccontrol/vcsserver-1/profile/bin/svnserve')
58 50
59 51
60 52 def includeme(config):
61 53 settings = config.registry.settings
62 54 _sanitize_settings_and_apply_defaults(settings)
63 55
64 56 # if we have enable generation of file, subscribe to event
65 57 if settings[config_keys.generate_authorized_keyfile]:
66 58 config.add_subscriber(
67 59 generate_ssh_authorized_keys_file_subscriber, SshKeyFileChangeEvent)
@@ -1,90 +1,89 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20 import os
21 21 import logging
22 import shlex
23 22 from pyramid import compat
24 23
25 24 # Do not use `from rhodecode import events` here, it will be overridden by the
26 25 # events module in this package due to pythons import mechanism.
27 26 from rhodecode.events import RepoGroupEvent
28 27 from rhodecode.subscribers import AsyncSubprocessSubscriber
29 from rhodecode.config.middleware import (
30 _bool_setting, _string_setting, _int_setting)
28 from rhodecode.config.settings_maker import SettingsMaker
31 29
32 30 from .events import ModDavSvnConfigChange
33 31 from .subscribers import generate_config_subscriber
34 32 from . import config_keys
35 33
36 34
37 35 log = logging.getLogger(__name__)
38 36
39 37
40 38 def _sanitize_settings_and_apply_defaults(settings):
41 39 """
42 40 Set defaults, convert to python types and validate settings.
43 41 """
44 _bool_setting(settings, config_keys.generate_config, 'false')
45 _bool_setting(settings, config_keys.list_parent_path, 'true')
46 _int_setting(settings, config_keys.reload_timeout, 10)
47 _string_setting(settings, config_keys.config_file_path, '', lower=False)
48 _string_setting(settings, config_keys.location_root, '/', lower=False)
49 _string_setting(settings, config_keys.reload_command, '', lower=False)
50 _string_setting(settings, config_keys.template, '', lower=False)
42 settings_maker = SettingsMaker(settings)
43 settings_maker.make_setting(config_keys.generate_config, False, parser='bool')
44 settings_maker.make_setting(config_keys.list_parent_path, True, parser='bool')
45 settings_maker.make_setting(config_keys.reload_timeout, 10, parser='bool')
46 settings_maker.make_setting(config_keys.config_file_path, '')
47 settings_maker.make_setting(config_keys.location_root, '/')
48 settings_maker.make_setting(config_keys.reload_command, '')
49 settings_maker.make_setting(config_keys.template, '')
51 50
52 51 # Convert negative timeout values to zero.
53 52 if settings[config_keys.reload_timeout] < 0:
54 53 settings[config_keys.reload_timeout] = 0
55 54
56 55 # Append path separator to location root.
57 56 settings[config_keys.location_root] = _append_path_sep(
58 57 settings[config_keys.location_root])
59 58
60 59 # Validate settings.
61 60 if settings[config_keys.generate_config]:
62 61 assert len(settings[config_keys.config_file_path]) > 0
63 62
64 63
65 64 def _append_path_sep(path):
66 65 """
67 66 Append the path separator if missing.
68 67 """
69 68 if isinstance(path, compat.string_types) and not path.endswith(os.path.sep):
70 69 path += os.path.sep
71 70 return path
72 71
73 72
74 73 def includeme(config):
75 74 settings = config.registry.settings
76 75 _sanitize_settings_and_apply_defaults(settings)
77 76
78 77 if settings[config_keys.generate_config]:
79 78 # Add subscriber to generate the Apache mod dav svn configuration on
80 79 # repository group events.
81 80 config.add_subscriber(generate_config_subscriber, RepoGroupEvent)
82 81
83 82 # If a reload command is set add a subscriber to execute it on
84 83 # configuration changes.
85 84 reload_cmd = settings[config_keys.reload_command]
86 85 if reload_cmd:
87 86 reload_timeout = settings[config_keys.reload_timeout] or None
88 87 reload_subscriber = AsyncSubprocessSubscriber(
89 88 cmd=reload_cmd, timeout=reload_timeout)
90 89 config.add_subscriber(reload_subscriber, ModDavSvnConfigChange)
@@ -1,122 +1,87 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import logging
23 23 import rhodecode
24 24
25 25 from rhodecode.config import utils
26 26
27 27 from rhodecode.lib.utils import load_rcextensions
28 28 from rhodecode.lib.utils2 import str2bool
29 29 from rhodecode.lib.vcs import connect_vcs
30 30
31 31 log = logging.getLogger(__name__)
32 32
33 33
34 34 def load_pyramid_environment(global_config, settings):
35 35 # Some parts of the code expect a merge of global and app settings.
36 36 settings_merged = global_config.copy()
37 37 settings_merged.update(settings)
38 38
39 39 # TODO(marcink): probably not required anymore
40 40 # configure channelstream,
41 41 settings_merged['channelstream_config'] = {
42 42 'enabled': str2bool(settings_merged.get('channelstream.enabled', False)),
43 43 'server': settings_merged.get('channelstream.server'),
44 44 'secret': settings_merged.get('channelstream.secret')
45 45 }
46 46
47 47 # If this is a test run we prepare the test environment like
48 48 # creating a test database, test search index and test repositories.
49 49 # This has to be done before the database connection is initialized.
50 50 if settings['is_test']:
51 51 rhodecode.is_test = True
52 52 rhodecode.disable_error_handler = True
53 53 from rhodecode import authentication
54 54 authentication.plugin_default_auth_ttl = 0
55 55
56 56 utils.initialize_test_environment(settings_merged)
57 57
58 58 # Initialize the database connection.
59 59 utils.initialize_database(settings_merged)
60 60
61 61 load_rcextensions(root_path=settings_merged['here'])
62 62
63 63 # Limit backends to `vcs.backends` from configuration, and preserve the order
64 64 for alias in rhodecode.BACKENDS.keys():
65 65 if alias not in settings['vcs.backends']:
66 66 del rhodecode.BACKENDS[alias]
67 67
68 68 _sorted_backend = sorted(rhodecode.BACKENDS.items(),
69 69 key=lambda item: settings['vcs.backends'].index(item[0]))
70 70 rhodecode.BACKENDS = rhodecode.OrderedDict(_sorted_backend)
71 71
72 72 log.info('Enabled VCS backends: %s', rhodecode.BACKENDS.keys())
73 73
74 74 # initialize vcs client and optionally run the server if enabled
75 75 vcs_server_uri = settings['vcs.server']
76 76 vcs_server_enabled = settings['vcs.server.enable']
77 77
78 78 utils.configure_vcs(settings)
79 79
80 80 # Store the settings to make them available to other modules.
81 81
82 82 rhodecode.PYRAMID_SETTINGS = settings_merged
83 83 rhodecode.CONFIG = settings_merged
84 84 rhodecode.CONFIG['default_user_id'] = utils.get_default_user_id()
85 85
86 86 if vcs_server_enabled:
87 87 connect_vcs(vcs_server_uri, utils.get_vcs_server_protocol(settings))
88
89
90 def get_rc_env_settings(prefix='ENV_{}'):
91 return {'ENV_{}'.format(key): value for key, value in os.environ.items()}
92
93
94 def substitute_values(mapping, substitutions):
95 result = {}
96
97 try:
98 for key, value in mapping.items():
99 # initialize without substitution first
100 result[key] = value
101
102 # Note: Cannot use regular replacements, since they would clash
103 # with the implementation of ConfigParser. Using "format" instead.
104 try:
105 result[key] = value.format(**substitutions)
106 except KeyError as e:
107 env_var = '{}'.format(e.args[0])
108
109 msg = 'Failed to substitute: `{key}={{{var}}}` with environment entry. ' \
110 'Make sure your environment has {var} set, or remove this ' \
111 'variable from config file'.format(key=key, var=env_var)
112
113 if env_var.startswith('ENV_'):
114 raise ValueError(msg)
115 else:
116 log.warning(msg)
117
118 except ValueError as e:
119 log.warning('Failed to substitute ENV variable: %s', e)
120 result = mapping
121
122 return result No newline at end of file
@@ -1,761 +1,611 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import sys
23 import logging
24 23 import collections
25 24 import tempfile
26 25 import time
26 import logging.config
27 27
28 28 from paste.gzipper import make_gzip_middleware
29 29 import pyramid.events
30 30 from pyramid.wsgi import wsgiapp
31 31 from pyramid.authorization import ACLAuthorizationPolicy
32 32 from pyramid.config import Configurator
33 33 from pyramid.settings import asbool, aslist
34 34 from pyramid.httpexceptions import (
35 35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
36 36 from pyramid.renderers import render_to_response
37 37
38 38 from rhodecode.model import meta
39 39 from rhodecode.config import patches
40 40 from rhodecode.config import utils as config_utils
41 from rhodecode.config.environment import load_pyramid_environment, substitute_values, get_rc_env_settings
41 from rhodecode.config.settings_maker import SettingsMaker
42 from rhodecode.config.environment import load_pyramid_environment
42 43
43 44 import rhodecode.events
44 45 from rhodecode.lib.middleware.vcs import VCSMiddleware
45 46 from rhodecode.lib.request import Request
46 47 from rhodecode.lib.vcs import VCSCommunicationError
47 48 from rhodecode.lib.exceptions import VCSServerUnavailable
48 49 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 50 from rhodecode.lib.middleware.https_fixup import HttpsFixup
50 51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
51 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
52 from rhodecode.lib.utils2 import AttributeDict
52 53 from rhodecode.lib.exc_tracking import store_exception
53 54 from rhodecode.subscribers import (
54 55 scan_repositories_if_enabled, write_js_routes_if_enabled,
55 56 write_metadata_if_needed, write_usage_data)
56 57 from rhodecode.lib.statsd_client import StatsdClient
57 58
58 59 log = logging.getLogger(__name__)
59 60
60 61
61 62 def is_http_error(response):
62 63 # error which should have traceback
63 64 return response.status_code > 499
64 65
65 66
66 67 def should_load_all():
67 68 """
68 69 Returns if all application components should be loaded. In some cases it's
69 70 desired to skip apps loading for faster shell script execution
70 71 """
71 72 ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER')
72 73 if ssh_cmd:
73 74 return False
74 75
75 76 return True
76 77
77 78
78 79 def make_pyramid_app(global_config, **settings):
79 80 """
80 81 Constructs the WSGI application based on Pyramid.
81 82
82 83 Specials:
83 84
84 85 * The application can also be integrated like a plugin via the call to
85 86 `includeme`. This is accompanied with the other utility functions which
86 87 are called. Changing this should be done with great care to not break
87 88 cases when these fragments are assembled from another place.
88 89
89 90 """
90
91 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
92 # will be replaced by the value of the environment variable "NAME" in this case.
93 91 start_time = time.time()
94 92 log.info('Pyramid app config starting')
95 93
96 global_config = substitute_values(global_config, get_rc_env_settings())
97 settings = substitute_values(settings, get_rc_env_settings())
94 sanitize_settings_and_apply_defaults(global_config, settings)
98 95
99 96 # init and bootstrap StatsdClient
100 97 StatsdClient.setup(settings)
101 98
102 debug = asbool(global_config.get('debug'))
103 if debug:
104 enable_debug()
105
106 sanitize_settings_and_apply_defaults(global_config, settings)
107
108 99 config = Configurator(settings=settings)
109 100 # Init our statsd at very start
110 101 config.registry.statsd = StatsdClient.statsd
111 102
112 103 # Apply compatibility patches
113 104 patches.inspect_getargspec()
114 105
115 106 load_pyramid_environment(global_config, settings)
116 107
117 108 # Static file view comes first
118 109 includeme_first(config)
119 110
120 111 includeme(config)
121 112
122 113 pyramid_app = config.make_wsgi_app()
123 114 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
124 115 pyramid_app.config = config
125 116
126 config.configure_celery(global_config['__file__'])
117 celery_settings = get_celery_config(settings)
118 config.configure_celery(celery_settings)
127 119
128 120 # creating the app uses a connection - return it after we are done
129 121 meta.Session.remove()
130 122
131 123 total_time = time.time() - start_time
132 124 log.info('Pyramid app `%s` created and configured in %.2fs',
133 pyramid_app.func_name, total_time)
125 getattr(pyramid_app, 'func_name', 'pyramid_app'), total_time)
134 126 return pyramid_app
135 127
136 128
129 def get_celery_config(settings):
130 """
131 Converts basic ini configuration into celery 4.X options
132 """
133
134 def key_converter(key_name):
135 pref = 'celery.'
136 if key_name.startswith(pref):
137 return key_name[len(pref):].replace('.', '_').lower()
138
139 def type_converter(parsed_key, value):
140 # cast to int
141 if value.isdigit():
142 return int(value)
143
144 # cast to bool
145 if value.lower() in ['true', 'false', 'True', 'False']:
146 return value.lower() == 'true'
147 return value
148
149 celery_config = {}
150 for k, v in settings.items():
151 pref = 'celery.'
152 if k.startswith(pref):
153 celery_config[key_converter(k)] = type_converter(key_converter(k), v)
154
155 # TODO:rethink if we want to support celerybeat based file config, probably NOT
156 # beat_config = {}
157 # for section in parser.sections():
158 # if section.startswith('celerybeat:'):
159 # name = section.split(':', 1)[1]
160 # beat_config[name] = get_beat_config(parser, section)
161
162 # final compose of settings
163 celery_settings = {}
164
165 if celery_config:
166 celery_settings.update(celery_config)
167 # if beat_config:
168 # celery_settings.update({'beat_schedule': beat_config})
169
170 return celery_settings
171
172
137 173 def not_found_view(request):
138 174 """
139 175 This creates the view which should be registered as not-found-view to
140 176 pyramid.
141 177 """
142 178
143 179 if not getattr(request, 'vcs_call', None):
144 180 # handle like regular case with our error_handler
145 181 return error_handler(HTTPNotFound(), request)
146 182
147 183 # handle not found view as a vcs call
148 184 settings = request.registry.settings
149 185 ae_client = getattr(request, 'ae_client', None)
150 186 vcs_app = VCSMiddleware(
151 187 HTTPNotFound(), request.registry, settings,
152 188 appenlight_client=ae_client)
153 189
154 190 return wsgiapp(vcs_app)(None, request)
155 191
156 192
157 193 def error_handler(exception, request):
158 194 import rhodecode
159 195 from rhodecode.lib import helpers
160 196 from rhodecode.lib.utils2 import str2bool
161 197
162 198 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
163 199
164 200 base_response = HTTPInternalServerError()
165 201 # prefer original exception for the response since it may have headers set
166 202 if isinstance(exception, HTTPException):
167 203 base_response = exception
168 204 elif isinstance(exception, VCSCommunicationError):
169 205 base_response = VCSServerUnavailable()
170 206
171 207 if is_http_error(base_response):
172 208 log.exception(
173 209 'error occurred handling this request for path: %s', request.path)
174 210
175 211 error_explanation = base_response.explanation or str(base_response)
176 212 if base_response.status_code == 404:
177 213 error_explanation += " Optionally you don't have permission to access this page."
178 214 c = AttributeDict()
179 215 c.error_message = base_response.status
180 216 c.error_explanation = error_explanation
181 217 c.visual = AttributeDict()
182 218
183 219 c.visual.rhodecode_support_url = (
184 220 request.registry.settings.get('rhodecode_support_url') or
185 221 request.route_url('rhodecode_support')
186 222 )
187 223 c.redirect_time = 0
188 224 c.rhodecode_name = rhodecode_title
189 225 if not c.rhodecode_name:
190 226 c.rhodecode_name = 'Rhodecode'
191 227
192 228 c.causes = []
193 229 if is_http_error(base_response):
194 230 c.causes.append('Server is overloaded.')
195 231 c.causes.append('Server database connection is lost.')
196 232 c.causes.append('Server expected unhandled error.')
197 233
198 234 if hasattr(base_response, 'causes'):
199 235 c.causes = base_response.causes
200 236
201 237 c.messages = helpers.flash.pop_messages(request=request)
202 238
203 239 exc_info = sys.exc_info()
204 240 c.exception_id = id(exc_info)
205 241 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
206 242 or base_response.status_code > 499
207 243 c.exception_id_url = request.route_url(
208 244 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
209 245
210 246 if c.show_exception_id:
211 247 store_exception(c.exception_id, exc_info)
212 248 c.exception_debug = str2bool(rhodecode.CONFIG.get('debug'))
213 249 c.exception_config_ini = rhodecode.CONFIG.get('__file__')
214 250
215 251 response = render_to_response(
216 252 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
217 253 response=base_response)
218 254
219 255 statsd = request.registry.statsd
220 256 if statsd and base_response.status_code > 499:
221 257 exc_type = "{}.{}".format(exception.__class__.__module__, exception.__class__.__name__)
222 258 statsd.incr('rhodecode_exception_total',
223 259 tags=["exc_source:web",
224 260 "http_code:{}".format(base_response.status_code),
225 261 "type:{}".format(exc_type)])
226 262
227 263 return response
228 264
229 265
230 266 def includeme_first(config):
231 267 # redirect automatic browser favicon.ico requests to correct place
232 268 def favicon_redirect(context, request):
233 269 return HTTPFound(
234 270 request.static_path('rhodecode:public/images/favicon.ico'))
235 271
236 272 config.add_view(favicon_redirect, route_name='favicon')
237 273 config.add_route('favicon', '/favicon.ico')
238 274
239 275 def robots_redirect(context, request):
240 276 return HTTPFound(
241 277 request.static_path('rhodecode:public/robots.txt'))
242 278
243 279 config.add_view(robots_redirect, route_name='robots')
244 280 config.add_route('robots', '/robots.txt')
245 281
246 282 config.add_static_view(
247 283 '_static/deform', 'deform:static')
248 284 config.add_static_view(
249 285 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
250 286
251 287
252 288 def includeme(config, auth_resources=None):
253 289 from rhodecode.lib.celerylib.loader import configure_celery
254 290 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
255 291 settings = config.registry.settings
256 292 config.set_request_factory(Request)
257 293
258 294 # plugin information
259 295 config.registry.rhodecode_plugins = collections.OrderedDict()
260 296
261 297 config.add_directive(
262 298 'register_rhodecode_plugin', register_rhodecode_plugin)
263 299
264 300 config.add_directive('configure_celery', configure_celery)
265 301
266 if asbool(settings.get('appenlight', 'false')):
302 if settings.get('appenlight', False):
267 303 config.include('appenlight_client.ext.pyramid_tween')
268 304
269 305 load_all = should_load_all()
270 306
271 307 # Includes which are required. The application would fail without them.
272 308 config.include('pyramid_mako')
273 309 config.include('rhodecode.lib.rc_beaker')
274 310 config.include('rhodecode.lib.rc_cache')
275 311 config.include('rhodecode.apps._base.navigation')
276 312 config.include('rhodecode.apps._base.subscribers')
277 313 config.include('rhodecode.tweens')
278 314 config.include('rhodecode.authentication')
279 315
280 316 if load_all:
281 317 ce_auth_resources = [
282 318 'rhodecode.authentication.plugins.auth_crowd',
283 319 'rhodecode.authentication.plugins.auth_headers',
284 320 'rhodecode.authentication.plugins.auth_jasig_cas',
285 321 'rhodecode.authentication.plugins.auth_ldap',
286 322 'rhodecode.authentication.plugins.auth_pam',
287 323 'rhodecode.authentication.plugins.auth_rhodecode',
288 324 'rhodecode.authentication.plugins.auth_token',
289 325 ]
290 326
291 327 # load CE authentication plugins
292 328
293 329 if auth_resources:
294 330 ce_auth_resources.extend(auth_resources)
295 331
296 332 for resource in ce_auth_resources:
297 333 config.include(resource)
298 334
299 335 # Auto discover authentication plugins and include their configuration.
300 336 if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')):
301 337 from rhodecode.authentication import discover_legacy_plugins
302 338 discover_legacy_plugins(config)
303 339
304 340 # apps
305 341 if load_all:
306 342 config.include('rhodecode.api')
307 343 config.include('rhodecode.apps._base')
308 344 config.include('rhodecode.apps.hovercards')
309 345 config.include('rhodecode.apps.ops')
310 346 config.include('rhodecode.apps.channelstream')
311 347 config.include('rhodecode.apps.file_store')
312 348 config.include('rhodecode.apps.admin')
313 349 config.include('rhodecode.apps.login')
314 350 config.include('rhodecode.apps.home')
315 351 config.include('rhodecode.apps.journal')
316 352
317 353 config.include('rhodecode.apps.repository')
318 354 config.include('rhodecode.apps.repo_group')
319 355 config.include('rhodecode.apps.user_group')
320 356 config.include('rhodecode.apps.search')
321 357 config.include('rhodecode.apps.user_profile')
322 358 config.include('rhodecode.apps.user_group_profile')
323 359 config.include('rhodecode.apps.my_account')
324 360 config.include('rhodecode.apps.gist')
325 361
326 362 config.include('rhodecode.apps.svn_support')
327 363 config.include('rhodecode.apps.ssh_support')
328 364 config.include('rhodecode.apps.debug_style')
329 365
330 366 if load_all:
331 367 config.include('rhodecode.integrations')
332 368
333 369 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
334 370 config.add_translation_dirs('rhodecode:i18n/')
335 371 settings['default_locale_name'] = settings.get('lang', 'en')
336 372
337 373 # Add subscribers.
338 374 if load_all:
339 375 config.add_subscriber(scan_repositories_if_enabled,
340 376 pyramid.events.ApplicationCreated)
341 377 config.add_subscriber(write_metadata_if_needed,
342 378 pyramid.events.ApplicationCreated)
343 379 config.add_subscriber(write_usage_data,
344 380 pyramid.events.ApplicationCreated)
345 381 config.add_subscriber(write_js_routes_if_enabled,
346 382 pyramid.events.ApplicationCreated)
347 383
348 384 # request custom methods
349 385 config.add_request_method(
350 386 'rhodecode.lib.partial_renderer.get_partial_renderer',
351 387 'get_partial_renderer')
352 388
353 389 config.add_request_method(
354 390 'rhodecode.lib.request_counter.get_request_counter',
355 391 'request_count')
356 392
357 393 # Set the authorization policy.
358 394 authz_policy = ACLAuthorizationPolicy()
359 395 config.set_authorization_policy(authz_policy)
360 396
361 397 # Set the default renderer for HTML templates to mako.
362 398 config.add_mako_renderer('.html')
363 399
364 400 config.add_renderer(
365 401 name='json_ext',
366 402 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
367 403
368 404 config.add_renderer(
369 405 name='string_html',
370 406 factory='rhodecode.lib.string_renderer.html')
371 407
372 408 # include RhodeCode plugins
373 409 includes = aslist(settings.get('rhodecode.includes', []))
374 410 for inc in includes:
375 411 config.include(inc)
376 412
377 413 # custom not found view, if our pyramid app doesn't know how to handle
378 414 # the request pass it to potential VCS handling ap
379 415 config.add_notfound_view(not_found_view)
380 416 if not settings.get('debugtoolbar.enabled', False):
381 417 # disabled debugtoolbar handle all exceptions via the error_handlers
382 418 config.add_view(error_handler, context=Exception)
383 419
384 420 # all errors including 403/404/50X
385 421 config.add_view(error_handler, context=HTTPError)
386 422
387 423
388 424 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
389 425 """
390 426 Apply outer WSGI middlewares around the application.
391 427 """
392 428 registry = config.registry
393 429 settings = registry.settings
394 430
395 431 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
396 432 pyramid_app = HttpsFixup(pyramid_app, settings)
397 433
398 434 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
399 435 pyramid_app, settings)
400 436 registry.ae_client = _ae_client
401 437
402 438 if settings['gzip_responses']:
403 439 pyramid_app = make_gzip_middleware(
404 440 pyramid_app, settings, compress_level=1)
405 441
406 442 # this should be the outer most middleware in the wsgi stack since
407 443 # middleware like Routes make database calls
408 444 def pyramid_app_with_cleanup(environ, start_response):
409 445 try:
410 446 return pyramid_app(environ, start_response)
411 447 finally:
412 448 # Dispose current database session and rollback uncommitted
413 449 # transactions.
414 450 meta.Session.remove()
415 451
416 452 # In a single threaded mode server, on non sqlite db we should have
417 453 # '0 Current Checked out connections' at the end of a request,
418 454 # if not, then something, somewhere is leaving a connection open
419 455 pool = meta.Base.metadata.bind.engine.pool
420 456 log.debug('sa pool status: %s', pool.status())
421 457 log.debug('Request processing finalized')
422 458
423 459 return pyramid_app_with_cleanup
424 460
425 461
426 462 def sanitize_settings_and_apply_defaults(global_config, settings):
427 463 """
428 464 Applies settings defaults and does all type conversion.
429 465
430 466 We would move all settings parsing and preparation into this place, so that
431 467 we have only one place left which deals with this part. The remaining parts
432 468 of the application would start to rely fully on well prepared settings.
433 469
434 470 This piece would later be split up per topic to avoid a big fat monster
435 471 function.
436 472 """
437 473
438 settings.setdefault('rhodecode.edition', 'Community Edition')
439 settings.setdefault('rhodecode.edition_id', 'CE')
474 global_settings_maker = SettingsMaker(global_config)
475 global_settings_maker.make_setting('debug', default=False, parser='bool')
476 debug_enabled = asbool(global_config.get('debug'))
477
478 settings_maker = SettingsMaker(settings)
479
480 settings_maker.make_setting(
481 'logging.autoconfigure',
482 default=True,
483 parser='bool')
484
485 logging_conf = os.path.join(os.path.dirname(global_config.get('__file__')), 'logging.ini')
486 settings_maker.enable_logging(logging_conf, level='INFO' if debug_enabled else 'DEBUG')
487
488 # Default includes, possible to change as a user
489 pyramid_includes = settings_maker.make_setting('pyramid.includes', [], parser='list:newline')
490 log.debug(
491 "Using the following pyramid.includes: %s",
492 pyramid_includes)
493
494 settings_maker.make_setting('rhodecode.edition', 'Community Edition')
495 settings_maker.make_setting('rhodecode.edition_id', 'CE')
440 496
441 497 if 'mako.default_filters' not in settings:
442 498 # set custom default filters if we don't have it defined
443 499 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
444 500 settings['mako.default_filters'] = 'h_filter'
445 501
446 502 if 'mako.directories' not in settings:
447 503 mako_directories = settings.setdefault('mako.directories', [
448 504 # Base templates of the original application
449 505 'rhodecode:templates',
450 506 ])
451 507 log.debug(
452 508 "Using the following Mako template directories: %s",
453 509 mako_directories)
454 510
455 511 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
456 512 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
457 513 raw_url = settings['beaker.session.url']
458 514 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
459 515 settings['beaker.session.url'] = 'redis://' + raw_url
460 516
461 # Default includes, possible to change as a user
462 pyramid_includes = settings.setdefault('pyramid.includes', [])
463 log.debug(
464 "Using the following pyramid.includes: %s",
465 pyramid_includes)
517 settings_maker.make_setting('__file__', global_config.get('__file__'))
466 518
467 519 # TODO: johbo: Re-think this, usually the call to config.include
468 520 # should allow to pass in a prefix.
469 settings.setdefault('rhodecode.api.url', '/_admin/api')
470 settings.setdefault('__file__', global_config.get('__file__'))
521 settings_maker.make_setting('rhodecode.api.url', '/_admin/api')
471 522
472 523 # Sanitize generic settings.
473 _list_setting(settings, 'default_encoding', 'UTF-8')
474 _bool_setting(settings, 'is_test', 'false')
475 _bool_setting(settings, 'gzip_responses', 'false')
524 settings_maker.make_setting('default_encoding', 'UTF-8', parser='list')
525 settings_maker.make_setting('is_test', False, parser='bool')
526 settings_maker.make_setting('gzip_responses', False, parser='bool')
476 527
477 # Call split out functions that sanitize settings for each topic.
478 _sanitize_appenlight_settings(settings)
479 _sanitize_vcs_settings(settings)
528 settings_maker.make_setting('vcs.svn.compatible_version', '')
529 settings_maker.make_setting('vcs.hooks.protocol', 'http')
530 settings_maker.make_setting('vcs.hooks.host', '127.0.0.1')
531 settings_maker.make_setting('vcs.scm_app_implementation', 'http')
532 settings_maker.make_setting('vcs.server', '')
533 settings_maker.make_setting('vcs.server.protocol', 'http')
534 settings_maker.make_setting('startup.import_repos', 'false', parser='bool')
535 settings_maker.make_setting('vcs.hooks.direct_calls', 'false', parser='bool')
536 settings_maker.make_setting('vcs.server.enable', 'true', parser='bool')
537 settings_maker.make_setting('vcs.start_server', 'false', parser='bool')
538 settings_maker.make_setting('vcs.backends', 'hg, git, svn', parser='list')
539 settings_maker.make_setting('vcs.connection_timeout', 3600, parser='int')
540
541 settings_maker.make_setting('vcs.methods.cache', True, parser='bool')
542
543 # Support legacy values of vcs.scm_app_implementation. Legacy
544 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
545 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
546 scm_app_impl = settings['vcs.scm_app_implementation']
547 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
548 settings['vcs.scm_app_implementation'] = 'http'
549
550 settings_maker.make_setting('appenlight', False, parser='bool')
551
480 552 _sanitize_cache_settings(settings)
481 553
482 554 # configure instance id
483 555 config_utils.set_instance_id(settings)
484 556
485 557 return settings
486 558
487 559
488 def enable_debug():
489 """
490 Helper to enable debug on running instance
491 :return:
492 """
493 import tempfile
494 import textwrap
495 import logging.config
496
497 ini_template = textwrap.dedent("""
498 #####################################
499 ### DEBUG LOGGING CONFIGURATION ####
500 #####################################
501 [loggers]
502 keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper
503
504 [handlers]
505 keys = console, console_sql
506
507 [formatters]
508 keys = generic, color_formatter, color_formatter_sql
509
510 #############
511 ## LOGGERS ##
512 #############
513 [logger_root]
514 level = NOTSET
515 handlers = console
516
517 [logger_sqlalchemy]
518 level = INFO
519 handlers = console_sql
520 qualname = sqlalchemy.engine
521 propagate = 0
522
523 [logger_beaker]
524 level = DEBUG
525 handlers =
526 qualname = beaker.container
527 propagate = 1
528
529 [logger_rhodecode]
530 level = DEBUG
531 handlers =
532 qualname = rhodecode
533 propagate = 1
534
535 [logger_ssh_wrapper]
536 level = DEBUG
537 handlers =
538 qualname = ssh_wrapper
539 propagate = 1
540
541 [logger_celery]
542 level = DEBUG
543 handlers =
544 qualname = celery
545
546
547 ##############
548 ## HANDLERS ##
549 ##############
550
551 [handler_console]
552 class = StreamHandler
553 args = (sys.stderr, )
554 level = DEBUG
555 formatter = color_formatter
560 def _sanitize_cache_settings(settings):
561 settings_maker = SettingsMaker(settings)
556 562
557 [handler_console_sql]
558 # "level = DEBUG" logs SQL queries and results.
559 # "level = INFO" logs SQL queries.
560 # "level = WARN" logs neither. (Recommended for production systems.)
561 class = StreamHandler
562 args = (sys.stderr, )
563 level = WARN
564 formatter = color_formatter_sql
565
566 ################
567 ## FORMATTERS ##
568 ################
569
570 [formatter_generic]
571 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
572 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
573 datefmt = %Y-%m-%d %H:%M:%S
574
575 [formatter_color_formatter]
576 class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter
577 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s
578 datefmt = %Y-%m-%d %H:%M:%S
579
580 [formatter_color_formatter_sql]
581 class = rhodecode.lib.logging_formatter.ColorFormatterSql
582 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
583 datefmt = %Y-%m-%d %H:%M:%S
584 """)
585
586 with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini',
587 delete=False) as f:
588 log.info('Saved Temporary DEBUG config at %s', f.name)
589 f.write(ini_template)
590
591 logging.config.fileConfig(f.name)
592 log.debug('DEBUG MODE ON')
593 os.remove(f.name)
594
595
596 def _sanitize_appenlight_settings(settings):
597 _bool_setting(settings, 'appenlight', 'false')
598
599
600 def _sanitize_vcs_settings(settings):
601 """
602 Applies settings defaults and does type conversion for all VCS related
603 settings.
604 """
605 _string_setting(settings, 'vcs.svn.compatible_version', '')
606 _string_setting(settings, 'vcs.hooks.protocol', 'http')
607 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
608 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
609 _string_setting(settings, 'vcs.server', '')
610 _string_setting(settings, 'vcs.server.protocol', 'http')
611 _bool_setting(settings, 'startup.import_repos', 'false')
612 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
613 _bool_setting(settings, 'vcs.server.enable', 'true')
614 _bool_setting(settings, 'vcs.start_server', 'false')
615 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
616 _int_setting(settings, 'vcs.connection_timeout', 3600)
617
618 # Support legacy values of vcs.scm_app_implementation. Legacy
619 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
620 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
621 scm_app_impl = settings['vcs.scm_app_implementation']
622 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
623 settings['vcs.scm_app_implementation'] = 'http'
624
625
626 def _sanitize_cache_settings(settings):
627 563 temp_store = tempfile.gettempdir()
628 564 default_cache_dir = os.path.join(temp_store, 'rc_cache')
629 565
630 566 # save default, cache dir, and use it for all backends later.
631 default_cache_dir = _string_setting(
632 settings,
567 default_cache_dir = settings_maker.make_setting(
633 568 'cache_dir',
634 default_cache_dir, lower=False, default_when_empty=True)
635
636 # ensure we have our dir created
637 if not os.path.isdir(default_cache_dir):
638 os.makedirs(default_cache_dir, mode=0o755)
569 default=default_cache_dir, default_when_empty=True,
570 parser='dir:ensured')
639 571
640 572 # exception store cache
641 _string_setting(
642 settings,
573 settings_maker.make_setting(
643 574 'exception_tracker.store_path',
644 temp_store, lower=False, default_when_empty=True)
645 _bool_setting(
646 settings,
647 'exception_tracker.send_email',
648 'false')
649 _string_setting(
650 settings,
651 'exception_tracker.email_prefix',
652 '[RHODECODE ERROR]', lower=False, default_when_empty=True)
575 default=os.path.join(default_cache_dir, 'exc_store'), default_when_empty=True,
576 parser='dir:ensured'
577 )
578
579 settings_maker.make_setting(
580 'celerybeat-schedule.path',
581 default=os.path.join(default_cache_dir, 'celerybeat_schedule', 'celerybeat-schedule.db'), default_when_empty=True,
582 parser='file:ensured'
583 )
584
585 settings_maker.make_setting('exception_tracker.send_email', False, parser='bool')
586 settings_maker.make_setting('exception_tracker.email_prefix', '[RHODECODE ERROR]', default_when_empty=True)
653 587
654 588 # cache_perms
655 _string_setting(
656 settings,
657 'rc_cache.cache_perms.backend',
658 'dogpile.cache.rc.file_namespace', lower=False)
659 _int_setting(
660 settings,
661 'rc_cache.cache_perms.expiration_time',
662 60)
663 _string_setting(
664 settings,
665 'rc_cache.cache_perms.arguments.filename',
666 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
589 settings_maker.make_setting('rc_cache.cache_perms.backend', 'dogpile.cache.rc.file_namespace')
590 settings_maker.make_setting('rc_cache.cache_perms.expiration_time', 60, parser='int')
591 settings_maker.make_setting('rc_cache.cache_perms.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_perms.db'))
667 592
668 593 # cache_repo
669 _string_setting(
670 settings,
671 'rc_cache.cache_repo.backend',
672 'dogpile.cache.rc.file_namespace', lower=False)
673 _int_setting(
674 settings,
675 'rc_cache.cache_repo.expiration_time',
676 60)
677 _string_setting(
678 settings,
679 'rc_cache.cache_repo.arguments.filename',
680 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
594 settings_maker.make_setting('rc_cache.cache_repo.backend', 'dogpile.cache.rc.file_namespace')
595 settings_maker.make_setting('rc_cache.cache_repo.expiration_time', 60, parser='int')
596 settings_maker.make_setting('rc_cache.cache_repo.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_repo.db'))
681 597
682 598 # cache_license
683 _string_setting(
684 settings,
685 'rc_cache.cache_license.backend',
686 'dogpile.cache.rc.file_namespace', lower=False)
687 _int_setting(
688 settings,
689 'rc_cache.cache_license.expiration_time',
690 5*60)
691 _string_setting(
692 settings,
693 'rc_cache.cache_license.arguments.filename',
694 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
599 settings_maker.make_setting('rc_cache.cache_license.backend', 'dogpile.cache.rc.file_namespace')
600 settings_maker.make_setting('rc_cache.cache_license.expiration_time', 5*60, parser='int')
601 settings_maker.make_setting('rc_cache.cache_license.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_license.db'))
695 602
696 603 # cache_repo_longterm memory, 96H
697 _string_setting(
698 settings,
699 'rc_cache.cache_repo_longterm.backend',
700 'dogpile.cache.rc.memory_lru', lower=False)
701 _int_setting(
702 settings,
703 'rc_cache.cache_repo_longterm.expiration_time',
704 345600)
705 _int_setting(
706 settings,
707 'rc_cache.cache_repo_longterm.max_size',
708 10000)
604 settings_maker.make_setting('rc_cache.cache_repo_longterm.backend', 'dogpile.cache.rc.memory_lru')
605 settings_maker.make_setting('rc_cache.cache_repo_longterm.expiration_time', 345600, parser='int')
606 settings_maker.make_setting('rc_cache.cache_repo_longterm.max_size', 10000, parser='int')
709 607
710 608 # sql_cache_short
711 _string_setting(
712 settings,
713 'rc_cache.sql_cache_short.backend',
714 'dogpile.cache.rc.memory_lru', lower=False)
715 _int_setting(
716 settings,
717 'rc_cache.sql_cache_short.expiration_time',
718 30)
719 _int_setting(
720 settings,
721 'rc_cache.sql_cache_short.max_size',
722 10000)
723
724
725 def _int_setting(settings, name, default):
726 settings[name] = int(settings.get(name, default))
727 return settings[name]
728
729
730 def _bool_setting(settings, name, default):
731 input_val = settings.get(name, default)
732 if isinstance(input_val, unicode):
733 input_val = input_val.encode('utf8')
734 settings[name] = asbool(input_val)
735 return settings[name]
736
737
738 def _list_setting(settings, name, default):
739 raw_value = settings.get(name, default)
740
741 old_separator = ','
742 if old_separator in raw_value:
743 # If we get a comma separated list, pass it to our own function.
744 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
745 else:
746 # Otherwise we assume it uses pyramids space/newline separation.
747 settings[name] = aslist(raw_value)
748 return settings[name]
749
750
751 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
752 value = settings.get(name, default)
753
754 if default_when_empty and not value:
755 # use default value when value is empty
756 value = default
757
758 if lower:
759 value = value.lower()
760 settings[name] = value
761 return settings[name]
609 settings_maker.make_setting('rc_cache.sql_cache_short.backend', 'dogpile.cache.rc.memory_lru')
610 settings_maker.make_setting('rc_cache.sql_cache_short.expiration_time', 30, parser='int')
611 settings_maker.make_setting('rc_cache.sql_cache_short.max_size', 10000, parser='int')
@@ -1,627 +1,627 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 The base Controller API
23 23 Provides the BaseController class for subclassing. And usage in different
24 24 controllers
25 25 """
26 26
27 27 import logging
28 28 import socket
29 29
30 30 import markupsafe
31 31 import ipaddress
32 32
33 33 from paste.auth.basic import AuthBasicAuthenticator
34 34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
35 35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
36 36
37 37 import rhodecode
38 38 from rhodecode.apps._base import TemplateArgs
39 39 from rhodecode.authentication.base import VCS_TYPE
40 40 from rhodecode.lib import auth, utils2
41 41 from rhodecode.lib import helpers as h
42 42 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
43 43 from rhodecode.lib.exceptions import UserCreationError
44 44 from rhodecode.lib.utils import (password_changed, get_enabled_hook_classes)
45 45 from rhodecode.lib.utils2 import (
46 46 str2bool, safe_unicode, AttributeDict, safe_int, sha1, aslist, safe_str)
47 47 from rhodecode.model.db import Repository, User, ChangesetComment, UserBookmark
48 48 from rhodecode.model.notification import NotificationModel
49 49 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
50 50
51 51 log = logging.getLogger(__name__)
52 52
53 53
54 54 def _filter_proxy(ip):
55 55 """
56 56 Passed in IP addresses in HEADERS can be in a special format of multiple
57 57 ips. Those comma separated IPs are passed from various proxies in the
58 58 chain of request processing. The left-most being the original client.
59 59 We only care about the first IP which came from the org. client.
60 60
61 61 :param ip: ip string from headers
62 62 """
63 63 if ',' in ip:
64 64 _ips = ip.split(',')
65 65 _first_ip = _ips[0].strip()
66 66 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
67 67 return _first_ip
68 68 return ip
69 69
70 70
71 71 def _filter_port(ip):
72 72 """
73 73 Removes a port from ip, there are 4 main cases to handle here.
74 74 - ipv4 eg. 127.0.0.1
75 75 - ipv6 eg. ::1
76 76 - ipv4+port eg. 127.0.0.1:8080
77 77 - ipv6+port eg. [::1]:8080
78 78
79 79 :param ip:
80 80 """
81 81 def is_ipv6(ip_addr):
82 82 if hasattr(socket, 'inet_pton'):
83 83 try:
84 84 socket.inet_pton(socket.AF_INET6, ip_addr)
85 85 except socket.error:
86 86 return False
87 87 else:
88 88 # fallback to ipaddress
89 89 try:
90 90 ipaddress.IPv6Address(safe_unicode(ip_addr))
91 91 except Exception:
92 92 return False
93 93 return True
94 94
95 95 if ':' not in ip: # must be ipv4 pure ip
96 96 return ip
97 97
98 98 if '[' in ip and ']' in ip: # ipv6 with port
99 99 return ip.split(']')[0][1:].lower()
100 100
101 101 # must be ipv6 or ipv4 with port
102 102 if is_ipv6(ip):
103 103 return ip
104 104 else:
105 105 ip, _port = ip.split(':')[:2] # means ipv4+port
106 106 return ip
107 107
108 108
109 109 def get_ip_addr(environ):
110 110 proxy_key = 'HTTP_X_REAL_IP'
111 111 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
112 112 def_key = 'REMOTE_ADDR'
113 113 _filters = lambda x: _filter_port(_filter_proxy(x))
114 114
115 115 ip = environ.get(proxy_key)
116 116 if ip:
117 117 return _filters(ip)
118 118
119 119 ip = environ.get(proxy_key2)
120 120 if ip:
121 121 return _filters(ip)
122 122
123 123 ip = environ.get(def_key, '0.0.0.0')
124 124 return _filters(ip)
125 125
126 126
127 127 def get_server_ip_addr(environ, log_errors=True):
128 128 hostname = environ.get('SERVER_NAME')
129 129 try:
130 130 return socket.gethostbyname(hostname)
131 131 except Exception as e:
132 132 if log_errors:
133 133 # in some cases this lookup is not possible, and we don't want to
134 134 # make it an exception in logs
135 135 log.exception('Could not retrieve server ip address: %s', e)
136 136 return hostname
137 137
138 138
139 139 def get_server_port(environ):
140 140 return environ.get('SERVER_PORT')
141 141
142 142
143 143 def get_access_path(environ):
144 144 path = environ.get('PATH_INFO')
145 145 org_req = environ.get('pylons.original_request')
146 146 if org_req:
147 147 path = org_req.environ.get('PATH_INFO')
148 148 return path
149 149
150 150
151 151 def get_user_agent(environ):
152 152 return environ.get('HTTP_USER_AGENT')
153 153
154 154
155 155 def vcs_operation_context(
156 156 environ, repo_name, username, action, scm, check_locking=True,
157 157 is_shadow_repo=False, check_branch_perms=False, detect_force_push=False):
158 158 """
159 159 Generate the context for a vcs operation, e.g. push or pull.
160 160
161 161 This context is passed over the layers so that hooks triggered by the
162 162 vcs operation know details like the user, the user's IP address etc.
163 163
164 164 :param check_locking: Allows to switch of the computation of the locking
165 165 data. This serves mainly the need of the simplevcs middleware to be
166 166 able to disable this for certain operations.
167 167
168 168 """
169 169 # Tri-state value: False: unlock, None: nothing, True: lock
170 170 make_lock = None
171 171 locked_by = [None, None, None]
172 172 is_anonymous = username == User.DEFAULT_USER
173 173 user = User.get_by_username(username)
174 174 if not is_anonymous and check_locking:
175 175 log.debug('Checking locking on repository "%s"', repo_name)
176 176 repo = Repository.get_by_repo_name(repo_name)
177 177 make_lock, __, locked_by = repo.get_locking_state(
178 178 action, user.user_id)
179 179 user_id = user.user_id
180 180 settings_model = VcsSettingsModel(repo=repo_name)
181 181 ui_settings = settings_model.get_ui_settings()
182 182
183 183 # NOTE(marcink): This should be also in sync with
184 184 # rhodecode/apps/ssh_support/lib/backends/base.py:update_environment scm_data
185 185 store = [x for x in ui_settings if x.key == '/']
186 186 repo_store = ''
187 187 if store:
188 188 repo_store = store[0].value
189 189
190 190 scm_data = {
191 191 'ip': get_ip_addr(environ),
192 192 'username': username,
193 193 'user_id': user_id,
194 194 'action': action,
195 195 'repository': repo_name,
196 196 'scm': scm,
197 197 'config': rhodecode.CONFIG['__file__'],
198 198 'repo_store': repo_store,
199 199 'make_lock': make_lock,
200 200 'locked_by': locked_by,
201 201 'server_url': utils2.get_server_url(environ),
202 202 'user_agent': get_user_agent(environ),
203 203 'hooks': get_enabled_hook_classes(ui_settings),
204 204 'is_shadow_repo': is_shadow_repo,
205 205 'detect_force_push': detect_force_push,
206 206 'check_branch_perms': check_branch_perms,
207 207 }
208 208 return scm_data
209 209
210 210
211 211 class BasicAuth(AuthBasicAuthenticator):
212 212
213 213 def __init__(self, realm, authfunc, registry, auth_http_code=None,
214 214 initial_call_detection=False, acl_repo_name=None, rc_realm=''):
215 215 self.realm = realm
216 216 self.rc_realm = rc_realm
217 217 self.initial_call = initial_call_detection
218 218 self.authfunc = authfunc
219 219 self.registry = registry
220 220 self.acl_repo_name = acl_repo_name
221 221 self._rc_auth_http_code = auth_http_code
222 222
223 223 def _get_response_from_code(self, http_code):
224 224 try:
225 225 return get_exception(safe_int(http_code))
226 226 except Exception:
227 227 log.exception('Failed to fetch response for code %s', http_code)
228 228 return HTTPForbidden
229 229
230 230 def get_rc_realm(self):
231 231 return safe_str(self.rc_realm)
232 232
233 233 def build_authentication(self):
234 234 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
235 235 if self._rc_auth_http_code and not self.initial_call:
236 236 # return alternative HTTP code if alternative http return code
237 237 # is specified in RhodeCode config, but ONLY if it's not the
238 238 # FIRST call
239 239 custom_response_klass = self._get_response_from_code(
240 240 self._rc_auth_http_code)
241 241 return custom_response_klass(headers=head)
242 242 return HTTPUnauthorized(headers=head)
243 243
244 244 def authenticate(self, environ):
245 245 authorization = AUTHORIZATION(environ)
246 246 if not authorization:
247 247 return self.build_authentication()
248 248 (authmeth, auth) = authorization.split(' ', 1)
249 249 if 'basic' != authmeth.lower():
250 250 return self.build_authentication()
251 251 auth = auth.strip().decode('base64')
252 252 _parts = auth.split(':', 1)
253 253 if len(_parts) == 2:
254 254 username, password = _parts
255 255 auth_data = self.authfunc(
256 256 username, password, environ, VCS_TYPE,
257 257 registry=self.registry, acl_repo_name=self.acl_repo_name)
258 258 if auth_data:
259 259 return {'username': username, 'auth_data': auth_data}
260 260 if username and password:
261 261 # we mark that we actually executed authentication once, at
262 262 # that point we can use the alternative auth code
263 263 self.initial_call = False
264 264
265 265 return self.build_authentication()
266 266
267 267 __call__ = authenticate
268 268
269 269
270 270 def calculate_version_hash(config):
271 271 return sha1(
272 272 config.get('beaker.session.secret', '') +
273 273 rhodecode.__version__)[:8]
274 274
275 275
276 276 def get_current_lang(request):
277 277 # NOTE(marcink): remove after pyramid move
278 278 try:
279 279 return translation.get_lang()[0]
280 280 except:
281 281 pass
282 282
283 283 return getattr(request, '_LOCALE_', request.locale_name)
284 284
285 285
286 286 def attach_context_attributes(context, request, user_id=None, is_api=None):
287 287 """
288 288 Attach variables into template context called `c`.
289 289 """
290 290 config = request.registry.settings
291 291
292 292 rc_config = SettingsModel().get_all_settings(cache=True, from_request=False)
293 293 context.rc_config = rc_config
294 294 context.rhodecode_version = rhodecode.__version__
295 295 context.rhodecode_edition = config.get('rhodecode.edition')
296 296 context.rhodecode_edition_id = config.get('rhodecode.edition_id')
297 297 # unique secret + version does not leak the version but keep consistency
298 298 context.rhodecode_version_hash = calculate_version_hash(config)
299 299
300 300 # Default language set for the incoming request
301 301 context.language = get_current_lang(request)
302 302
303 303 # Visual options
304 304 context.visual = AttributeDict({})
305 305
306 306 # DB stored Visual Items
307 307 context.visual.show_public_icon = str2bool(
308 308 rc_config.get('rhodecode_show_public_icon'))
309 309 context.visual.show_private_icon = str2bool(
310 310 rc_config.get('rhodecode_show_private_icon'))
311 311 context.visual.stylify_metatags = str2bool(
312 312 rc_config.get('rhodecode_stylify_metatags'))
313 313 context.visual.dashboard_items = safe_int(
314 314 rc_config.get('rhodecode_dashboard_items', 100))
315 315 context.visual.admin_grid_items = safe_int(
316 316 rc_config.get('rhodecode_admin_grid_items', 100))
317 317 context.visual.show_revision_number = str2bool(
318 318 rc_config.get('rhodecode_show_revision_number', True))
319 319 context.visual.show_sha_length = safe_int(
320 320 rc_config.get('rhodecode_show_sha_length', 100))
321 321 context.visual.repository_fields = str2bool(
322 322 rc_config.get('rhodecode_repository_fields'))
323 323 context.visual.show_version = str2bool(
324 324 rc_config.get('rhodecode_show_version'))
325 325 context.visual.use_gravatar = str2bool(
326 326 rc_config.get('rhodecode_use_gravatar'))
327 327 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
328 328 context.visual.default_renderer = rc_config.get(
329 329 'rhodecode_markup_renderer', 'rst')
330 330 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
331 331 context.visual.rhodecode_support_url = \
332 332 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
333 333
334 334 context.visual.affected_files_cut_off = 60
335 335
336 336 context.pre_code = rc_config.get('rhodecode_pre_code')
337 337 context.post_code = rc_config.get('rhodecode_post_code')
338 338 context.rhodecode_name = rc_config.get('rhodecode_title')
339 339 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
340 340 # if we have specified default_encoding in the request, it has more
341 341 # priority
342 342 if request.GET.get('default_encoding'):
343 343 context.default_encodings.insert(0, request.GET.get('default_encoding'))
344 344 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
345 345 context.clone_uri_id_tmpl = rc_config.get('rhodecode_clone_uri_id_tmpl')
346 346 context.clone_uri_ssh_tmpl = rc_config.get('rhodecode_clone_uri_ssh_tmpl')
347 347
348 348 # INI stored
349 349 context.labs_active = str2bool(
350 350 config.get('labs_settings_active', 'false'))
351 351 context.ssh_enabled = str2bool(
352 352 config.get('ssh.generate_authorized_keyfile', 'false'))
353 353 context.ssh_key_generator_enabled = str2bool(
354 354 config.get('ssh.enable_ui_key_generator', 'true'))
355 355
356 356 context.visual.allow_repo_location_change = str2bool(
357 357 config.get('allow_repo_location_change', True))
358 358 context.visual.allow_custom_hooks_settings = str2bool(
359 359 config.get('allow_custom_hooks_settings', True))
360 360 context.debug_style = str2bool(config.get('debug_style', False))
361 361
362 362 context.rhodecode_instanceid = config.get('instance_id')
363 363
364 364 context.visual.cut_off_limit_diff = safe_int(
365 365 config.get('cut_off_limit_diff'))
366 366 context.visual.cut_off_limit_file = safe_int(
367 367 config.get('cut_off_limit_file'))
368 368
369 369 context.license = AttributeDict({})
370 370 context.license.hide_license_info = str2bool(
371 371 config.get('license.hide_license_info', False))
372 372
373 373 # AppEnlight
374 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
374 context.appenlight_enabled = config.get('appenlight', False)
375 375 context.appenlight_api_public_key = config.get(
376 376 'appenlight.api_public_key', '')
377 377 context.appenlight_server_url = config.get('appenlight.server_url', '')
378 378
379 379 diffmode = {
380 380 "unified": "unified",
381 381 "sideside": "sideside"
382 382 }.get(request.GET.get('diffmode'))
383 383
384 384 if is_api is not None:
385 385 is_api = hasattr(request, 'rpc_user')
386 386 session_attrs = {
387 387 # defaults
388 388 "clone_url_format": "http",
389 389 "diffmode": "sideside",
390 390 "license_fingerprint": request.session.get('license_fingerprint')
391 391 }
392 392
393 393 if not is_api:
394 394 # don't access pyramid session for API calls
395 395 if diffmode and diffmode != request.session.get('rc_user_session_attr.diffmode'):
396 396 request.session['rc_user_session_attr.diffmode'] = diffmode
397 397
398 398 # session settings per user
399 399
400 400 for k, v in request.session.items():
401 401 pref = 'rc_user_session_attr.'
402 402 if k and k.startswith(pref):
403 403 k = k[len(pref):]
404 404 session_attrs[k] = v
405 405
406 406 context.user_session_attrs = session_attrs
407 407
408 408 # JS template context
409 409 context.template_context = {
410 410 'repo_name': None,
411 411 'repo_type': None,
412 412 'repo_landing_commit': None,
413 413 'rhodecode_user': {
414 414 'username': None,
415 415 'email': None,
416 416 'notification_status': False
417 417 },
418 418 'session_attrs': session_attrs,
419 419 'visual': {
420 420 'default_renderer': None
421 421 },
422 422 'commit_data': {
423 423 'commit_id': None
424 424 },
425 425 'pull_request_data': {'pull_request_id': None},
426 426 'timeago': {
427 427 'refresh_time': 120 * 1000,
428 428 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
429 429 },
430 430 'pyramid_dispatch': {
431 431
432 432 },
433 433 'extra': {'plugins': {}}
434 434 }
435 435 # END CONFIG VARS
436 436 if is_api:
437 437 csrf_token = None
438 438 else:
439 439 csrf_token = auth.get_csrf_token(session=request.session)
440 440
441 441 context.csrf_token = csrf_token
442 442 context.backends = rhodecode.BACKENDS.keys()
443 443
444 444 unread_count = 0
445 445 user_bookmark_list = []
446 446 if user_id:
447 447 unread_count = NotificationModel().get_unread_cnt_for_user(user_id)
448 448 user_bookmark_list = UserBookmark.get_bookmarks_for_user(user_id)
449 449 context.unread_notifications = unread_count
450 450 context.bookmark_items = user_bookmark_list
451 451
452 452 # web case
453 453 if hasattr(request, 'user'):
454 454 context.auth_user = request.user
455 455 context.rhodecode_user = request.user
456 456
457 457 # api case
458 458 if hasattr(request, 'rpc_user'):
459 459 context.auth_user = request.rpc_user
460 460 context.rhodecode_user = request.rpc_user
461 461
462 462 # attach the whole call context to the request
463 463 request.call_context = context
464 464
465 465
466 466 def get_auth_user(request):
467 467 environ = request.environ
468 468 session = request.session
469 469
470 470 ip_addr = get_ip_addr(environ)
471 471
472 472 # make sure that we update permissions each time we call controller
473 473 _auth_token = (
474 474 # ?auth_token=XXX
475 475 request.GET.get('auth_token', '')
476 476 # ?api_key=XXX !LEGACY
477 477 or request.GET.get('api_key', '')
478 478 # or headers....
479 479 or request.headers.get('X-Rc-Auth-Token', '')
480 480 )
481 481 if not _auth_token and request.matchdict:
482 482 url_auth_token = request.matchdict.get('_auth_token')
483 483 _auth_token = url_auth_token
484 484 if _auth_token:
485 485 log.debug('Using URL extracted auth token `...%s`', _auth_token[-4:])
486 486
487 487 if _auth_token:
488 488 # when using API_KEY we assume user exists, and
489 489 # doesn't need auth based on cookies.
490 490 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
491 491 authenticated = False
492 492 else:
493 493 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
494 494 try:
495 495 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
496 496 ip_addr=ip_addr)
497 497 except UserCreationError as e:
498 498 h.flash(e, 'error')
499 499 # container auth or other auth functions that create users
500 500 # on the fly can throw this exception signaling that there's
501 501 # issue with user creation, explanation should be provided
502 502 # in Exception itself. We then create a simple blank
503 503 # AuthUser
504 504 auth_user = AuthUser(ip_addr=ip_addr)
505 505
506 506 # in case someone changes a password for user it triggers session
507 507 # flush and forces a re-login
508 508 if password_changed(auth_user, session):
509 509 session.invalidate()
510 510 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
511 511 auth_user = AuthUser(ip_addr=ip_addr)
512 512
513 513 authenticated = cookie_store.get('is_authenticated')
514 514
515 515 if not auth_user.is_authenticated and auth_user.is_user_object:
516 516 # user is not authenticated and not empty
517 517 auth_user.set_authenticated(authenticated)
518 518
519 519 return auth_user, _auth_token
520 520
521 521
522 522 def h_filter(s):
523 523 """
524 524 Custom filter for Mako templates. Mako by standard uses `markupsafe.escape`
525 525 we wrap this with additional functionality that converts None to empty
526 526 strings
527 527 """
528 528 if s is None:
529 529 return markupsafe.Markup()
530 530 return markupsafe.escape(s)
531 531
532 532
533 533 def add_events_routes(config):
534 534 """
535 535 Adds routing that can be used in events. Because some events are triggered
536 536 outside of pyramid context, we need to bootstrap request with some
537 537 routing registered
538 538 """
539 539
540 540 from rhodecode.apps._base import ADMIN_PREFIX
541 541
542 542 config.add_route(name='home', pattern='/')
543 543 config.add_route(name='main_page_repos_data', pattern='/_home_repos')
544 544 config.add_route(name='main_page_repo_groups_data', pattern='/_home_repo_groups')
545 545
546 546 config.add_route(name='login', pattern=ADMIN_PREFIX + '/login')
547 547 config.add_route(name='logout', pattern=ADMIN_PREFIX + '/logout')
548 548 config.add_route(name='repo_summary', pattern='/{repo_name}')
549 549 config.add_route(name='repo_summary_explicit', pattern='/{repo_name}/summary')
550 550 config.add_route(name='repo_group_home', pattern='/{repo_group_name}')
551 551
552 552 config.add_route(name='pullrequest_show',
553 553 pattern='/{repo_name}/pull-request/{pull_request_id}')
554 554 config.add_route(name='pull_requests_global',
555 555 pattern='/pull-request/{pull_request_id}')
556 556
557 557 config.add_route(name='repo_commit',
558 558 pattern='/{repo_name}/changeset/{commit_id}')
559 559 config.add_route(name='repo_files',
560 560 pattern='/{repo_name}/files/{commit_id}/{f_path}')
561 561
562 562 config.add_route(name='hovercard_user',
563 563 pattern='/_hovercard/user/{user_id}')
564 564
565 565 config.add_route(name='hovercard_user_group',
566 566 pattern='/_hovercard/user_group/{user_group_id}')
567 567
568 568 config.add_route(name='hovercard_pull_request',
569 569 pattern='/_hovercard/pull_request/{pull_request_id}')
570 570
571 571 config.add_route(name='hovercard_repo_commit',
572 572 pattern='/_hovercard/commit/{repo_name}/{commit_id}')
573 573
574 574
575 575 def bootstrap_config(request):
576 576 import pyramid.testing
577 577 registry = pyramid.testing.Registry('RcTestRegistry')
578 578
579 579 config = pyramid.testing.setUp(registry=registry, request=request)
580 580
581 581 # allow pyramid lookup in testing
582 582 config.include('pyramid_mako')
583 583 config.include('rhodecode.lib.rc_beaker')
584 584 config.include('rhodecode.lib.rc_cache')
585 585
586 586 add_events_routes(config)
587 587
588 588 return config
589 589
590 590
591 591 def bootstrap_request(**kwargs):
592 592 import pyramid.testing
593 593
594 594 class TestRequest(pyramid.testing.DummyRequest):
595 595 application_url = kwargs.pop('application_url', 'http://example.com')
596 596 host = kwargs.pop('host', 'example.com:80')
597 597 domain = kwargs.pop('domain', 'example.com')
598 598
599 599 def translate(self, msg):
600 600 return msg
601 601
602 602 def plularize(self, singular, plural, n):
603 603 return singular
604 604
605 605 def get_partial_renderer(self, tmpl_name):
606 606
607 607 from rhodecode.lib.partial_renderer import get_partial_renderer
608 608 return get_partial_renderer(request=self, tmpl_name=tmpl_name)
609 609
610 610 _call_context = TemplateArgs()
611 611 _call_context.visual = TemplateArgs()
612 612 _call_context.visual.show_sha_length = 12
613 613 _call_context.visual.show_revision_number = True
614 614
615 615 @property
616 616 def call_context(self):
617 617 return self._call_context
618 618
619 619 class TestDummySession(pyramid.testing.DummySession):
620 620 def save(*arg, **kw):
621 621 pass
622 622
623 623 request = TestRequest(**kwargs)
624 624 request.session = TestDummySession()
625 625
626 626 return request
627 627
@@ -1,312 +1,313 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20 """
21 21 Celery loader, run with::
22 22
23 23 celery worker \
24 24 --beat \
25 25 --app rhodecode.lib.celerylib.loader \
26 26 --scheduler rhodecode.lib.celerylib.scheduler.RcScheduler \
27 27 --loglevel DEBUG --ini=._dev/dev.ini
28 28 """
29 29 import os
30 30 import logging
31 31 import importlib
32 32
33 33 from celery import Celery
34 34 from celery import signals
35 35 from celery import Task
36 36 from celery import exceptions # pragma: no cover
37 37 from kombu.serialization import register
38 38 from pyramid.threadlocal import get_current_request
39 39
40 40 import rhodecode
41 41
42 42 from rhodecode.lib.auth import AuthUser
43 from rhodecode.lib.celerylib.utils import get_ini_config, parse_ini_vars, ping_db
43 from rhodecode.lib.celerylib.utils import parse_ini_vars, ping_db
44 44 from rhodecode.lib.ext_json import json
45 45 from rhodecode.lib.pyramid_utils import bootstrap, setup_logging, prepare_request
46 46 from rhodecode.lib.utils2 import str2bool
47 47 from rhodecode.model import meta
48 48
49 49
50 50 register('json_ext', json.dumps, json.loads,
51 51 content_type='application/x-json-ext',
52 52 content_encoding='utf-8')
53 53
54 54 log = logging.getLogger('celery.rhodecode.loader')
55 55
56 56
57 57 def add_preload_arguments(parser):
58 58 parser.add_argument(
59 59 '--ini', default=None,
60 60 help='Path to ini configuration file.'
61 61 )
62 62 parser.add_argument(
63 63 '--ini-var', default=None,
64 64 help='Comma separated list of key=value to pass to ini.'
65 65 )
66 66
67 67
68 68 def get_logger(obj):
69 69 custom_log = logging.getLogger(
70 70 'rhodecode.task.{}'.format(obj.__class__.__name__))
71 71
72 72 if rhodecode.CELERY_ENABLED:
73 73 try:
74 74 custom_log = obj.get_logger()
75 75 except Exception:
76 76 pass
77 77
78 78 return custom_log
79 79
80 80
81 81 imports = ['rhodecode.lib.celerylib.tasks']
82 82
83 83 try:
84 84 # try if we have EE tasks available
85 85 importlib.import_module('rc_ee')
86 86 imports.append('rc_ee.lib.celerylib.tasks')
87 87 except ImportError:
88 88 pass
89 89
90 90
91 91 base_celery_config = {
92 92 'result_backend': 'rpc://',
93 93 'result_expires': 60 * 60 * 24,
94 94 'result_persistent': True,
95 95 'imports': imports,
96 96 'worker_max_tasks_per_child': 100,
97 97 'accept_content': ['json_ext'],
98 98 'task_serializer': 'json_ext',
99 99 'result_serializer': 'json_ext',
100 100 'worker_hijack_root_logger': False,
101 101 'database_table_names': {
102 102 'task': 'beat_taskmeta',
103 103 'group': 'beat_groupmeta',
104 104 }
105 105 }
106 106 # init main celery app
107 107 celery_app = Celery()
108 108 celery_app.user_options['preload'].add(add_preload_arguments)
109 109 ini_file_glob = None
110 110
111 111
112 112 @signals.setup_logging.connect
113 113 def setup_logging_callback(**kwargs):
114 114 setup_logging(ini_file_glob)
115 115
116 116
117 117 @signals.user_preload_options.connect
118 118 def on_preload_parsed(options, **kwargs):
119 from rhodecode.config.middleware import get_celery_config
120
119 121 ini_location = options['ini']
120 122 ini_vars = options['ini_var']
121 123 celery_app.conf['INI_PYRAMID'] = options['ini']
122 124
123 125 if ini_location is None:
124 126 print('You must provide the paste --ini argument')
125 127 exit(-1)
126 128
127 129 options = None
128 130 if ini_vars is not None:
129 131 options = parse_ini_vars(ini_vars)
130 132
131 133 global ini_file_glob
132 134 ini_file_glob = ini_location
133 135
134 136 log.debug('Bootstrapping RhodeCode application...')
135 137 env = bootstrap(ini_location, options=options)
136 138
139 celery_settings = get_celery_config(env['registry'].settings)
137 140 setup_celery_app(
138 141 app=env['app'], root=env['root'], request=env['request'],
139 142 registry=env['registry'], closer=env['closer'],
140 ini_location=ini_location)
143 celery_settings=celery_settings)
141 144
142 145 # fix the global flag even if it's disabled via .ini file because this
143 146 # is a worker code that doesn't need this to be disabled.
144 147 rhodecode.CELERY_ENABLED = True
145 148
146 149
147 150 @signals.task_prerun.connect
148 151 def task_prerun_signal(task_id, task, args, **kwargs):
149 152 ping_db()
150 153
151 154
152 155 @signals.task_success.connect
153 156 def task_success_signal(result, **kwargs):
154 157 meta.Session.commit()
155 158 closer = celery_app.conf['PYRAMID_CLOSER']
156 159 if closer:
157 160 closer()
158 161
159 162
160 163 @signals.task_retry.connect
161 164 def task_retry_signal(
162 165 request, reason, einfo, **kwargs):
163 166 meta.Session.remove()
164 167 closer = celery_app.conf['PYRAMID_CLOSER']
165 168 if closer:
166 169 closer()
167 170
168 171
169 172 @signals.task_failure.connect
170 173 def task_failure_signal(
171 174 task_id, exception, args, kwargs, traceback, einfo, **kargs):
172 175 from rhodecode.lib.exc_tracking import store_exception
173 176 from rhodecode.lib.statsd_client import StatsdClient
174 177
175 178 meta.Session.remove()
176 179
177 180 # simulate sys.exc_info()
178 181 exc_info = (einfo.type, einfo.exception, einfo.tb)
179 182 store_exception(id(exc_info), exc_info, prefix='rhodecode-celery')
180 183 statsd = StatsdClient.statsd
181 184 if statsd:
182 185 exc_type = "{}.{}".format(einfo.__class__.__module__, einfo.__class__.__name__)
183 186 statsd.incr('rhodecode_exception_total',
184 187 tags=["exc_source:celery", "type:{}".format(exc_type)])
185 188
186 189 closer = celery_app.conf['PYRAMID_CLOSER']
187 190 if closer:
188 191 closer()
189 192
190 193
191 194 @signals.task_revoked.connect
192 195 def task_revoked_signal(
193 196 request, terminated, signum, expired, **kwargs):
194 197 closer = celery_app.conf['PYRAMID_CLOSER']
195 198 if closer:
196 199 closer()
197 200
198 201
199 def setup_celery_app(app, root, request, registry, closer, ini_location):
200 ini_dir = os.path.dirname(os.path.abspath(ini_location))
202 def setup_celery_app(app, root, request, registry, closer, celery_settings):
203 log.debug('Got custom celery conf: %s', celery_settings)
201 204 celery_config = base_celery_config
202 205 celery_config.update({
203 206 # store celerybeat scheduler db where the .ini file is
204 'beat_schedule_filename': os.path.join(ini_dir, 'celerybeat-schedule'),
207 'beat_schedule_filename': registry.settings['celerybeat-schedule.path'],
205 208 })
206 ini_settings = get_ini_config(ini_location)
207 log.debug('Got custom celery conf: %s', ini_settings)
208 209
209 celery_config.update(ini_settings)
210 celery_config.update(celery_settings)
210 211 celery_app.config_from_object(celery_config)
211 212
212 213 celery_app.conf.update({'PYRAMID_APP': app})
213 214 celery_app.conf.update({'PYRAMID_ROOT': root})
214 215 celery_app.conf.update({'PYRAMID_REQUEST': request})
215 216 celery_app.conf.update({'PYRAMID_REGISTRY': registry})
216 217 celery_app.conf.update({'PYRAMID_CLOSER': closer})
217 218
218 219
219 def configure_celery(config, ini_location):
220 def configure_celery(config, celery_settings):
220 221 """
221 222 Helper that is called from our application creation logic. It gives
222 223 connection info into running webapp and allows execution of tasks from
223 224 RhodeCode itself
224 225 """
225 226 # store some globals into rhodecode
226 227 rhodecode.CELERY_ENABLED = str2bool(
227 228 config.registry.settings.get('use_celery'))
228 229 if rhodecode.CELERY_ENABLED:
229 log.info('Configuring celery based on `%s` file', ini_location)
230 log.info('Configuring celery based on `%s` settings', celery_settings)
230 231 setup_celery_app(
231 232 app=None, root=None, request=None, registry=config.registry,
232 closer=None, ini_location=ini_location)
233 closer=None, celery_settings=celery_settings)
233 234
234 235
235 236 def maybe_prepare_env(req):
236 237 environ = {}
237 238 try:
238 239 environ.update({
239 240 'PATH_INFO': req.environ['PATH_INFO'],
240 241 'SCRIPT_NAME': req.environ['SCRIPT_NAME'],
241 242 'HTTP_HOST':req.environ.get('HTTP_HOST', req.environ['SERVER_NAME']),
242 243 'SERVER_NAME': req.environ['SERVER_NAME'],
243 244 'SERVER_PORT': req.environ['SERVER_PORT'],
244 245 'wsgi.url_scheme': req.environ['wsgi.url_scheme'],
245 246 })
246 247 except Exception:
247 248 pass
248 249
249 250 return environ
250 251
251 252
252 253 class RequestContextTask(Task):
253 254 """
254 255 This is a celery task which will create a rhodecode app instance context
255 256 for the task, patch pyramid with the original request
256 257 that created the task and also add the user to the context.
257 258 """
258 259
259 260 def apply_async(self, args=None, kwargs=None, task_id=None, producer=None,
260 261 link=None, link_error=None, shadow=None, **options):
261 262 """ queue the job to run (we are in web request context here) """
262 263
263 264 req = get_current_request()
264 265
265 266 # web case
266 267 if hasattr(req, 'user'):
267 268 ip_addr = req.user.ip_addr
268 269 user_id = req.user.user_id
269 270
270 271 # api case
271 272 elif hasattr(req, 'rpc_user'):
272 273 ip_addr = req.rpc_user.ip_addr
273 274 user_id = req.rpc_user.user_id
274 275 else:
275 276 raise Exception(
276 277 'Unable to fetch required data from request: {}. \n'
277 278 'This task is required to be executed from context of '
278 279 'request in a webapp'.format(repr(req)))
279 280
280 281 if req:
281 282 # we hook into kwargs since it is the only way to pass our data to
282 283 # the celery worker
283 284 environ = maybe_prepare_env(req)
284 285 options['headers'] = options.get('headers', {})
285 286 options['headers'].update({
286 287 'rhodecode_proxy_data': {
287 288 'environ': environ,
288 289 'auth_user': {
289 290 'ip_addr': ip_addr,
290 291 'user_id': user_id
291 292 },
292 293 }
293 294 })
294 295
295 296 return super(RequestContextTask, self).apply_async(
296 297 args, kwargs, task_id, producer, link, link_error, shadow, **options)
297 298
298 299 def __call__(self, *args, **kwargs):
299 300 """ rebuild the context and then run task on celery worker """
300 301
301 302 proxy_data = getattr(self.request, 'rhodecode_proxy_data', None)
302 303 if not proxy_data:
303 304 return super(RequestContextTask, self).__call__(*args, **kwargs)
304 305
305 306 log.debug('using celery proxy data to run task: %r', proxy_data)
306 307 # re-inject and register threadlocals for proper routing support
307 308 request = prepare_request(proxy_data['environ'])
308 309 request.user = AuthUser(user_id=proxy_data['auth_user']['user_id'],
309 310 ip_addr=proxy_data['auth_user']['ip_addr'])
310 311
311 312 return super(RequestContextTask, self).__call__(*args, **kwargs)
312 313
@@ -1,187 +1,141 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import json
23 23 import logging
24 24 import datetime
25 25 import time
26 26
27 27 from functools import partial
28 28
29 29 from pyramid.compat import configparser
30 30 from celery.result import AsyncResult
31 31 import celery.loaders.base
32 32 import celery.schedules
33 33
34 34 log = logging.getLogger(__name__)
35 35
36 36
37 37 def get_task_id(task):
38 38 task_id = None
39 39 if isinstance(task, AsyncResult):
40 40 task_id = task.task_id
41 41
42 42 return task_id
43 43
44 44
45 45 def crontab(value):
46 46 return celery.schedules.crontab(**value)
47 47
48 48
49 49 def timedelta(value):
50 50 return datetime.timedelta(**value)
51 51
52 52
53 53 def safe_json(get, section, key):
54 54 value = ''
55 55 try:
56 56 value = get(key)
57 57 json_value = json.loads(value)
58 58 except ValueError:
59 59 msg = 'The %s=%s is not valid json in section %s' % (
60 60 key, value, section
61 61 )
62 62 raise ValueError(msg)
63 63
64 64 return json_value
65 65
66 66
67 67 def raw_2_schedule(schedule_value, schedule_type):
68 68 schedule_type_map = {
69 69 'crontab': crontab,
70 70 'timedelta': timedelta,
71 71 'integer': int
72 72 }
73 73 scheduler_cls = schedule_type_map.get(schedule_type)
74 74
75 75 if scheduler_cls is None:
76 76 raise ValueError(
77 77 'schedule type %s in section is invalid' % (
78 78 schedule_type,
79 79 )
80 80 )
81 81 try:
82 82 schedule = scheduler_cls(schedule_value)
83 83 except TypeError:
84 84 log.exception('Failed to compose a schedule from value: %r', schedule_value)
85 85 schedule = None
86 86 return schedule
87 87
88 88
89 89 def get_beat_config(parser, section):
90 90
91 91 get = partial(parser.get, section)
92 92 has_option = partial(parser.has_option, section)
93 93
94 94 schedule_type = get('type')
95 95 schedule_value = safe_json(get, section, 'schedule')
96 96
97 97 config = {
98 98 'schedule_type': schedule_type,
99 99 'schedule_value': schedule_value,
100 100 'task': get('task'),
101 101 }
102 102 schedule = raw_2_schedule(schedule_value, schedule_type)
103 103 if schedule:
104 104 config['schedule'] = schedule
105 105
106 106 if has_option('args'):
107 107 config['args'] = safe_json(get, section, 'args')
108 108
109 109 if has_option('kwargs'):
110 110 config['kwargs'] = safe_json(get, section, 'kwargs')
111 111
112 112 if has_option('force_update'):
113 113 config['force_update'] = get('force_update')
114 114
115 115 return config
116 116
117 117
118 def get_ini_config(ini_location):
119 """
120 Converts basic ini configuration into celery 4.X options
121 """
122 def key_converter(key_name):
123 pref = 'celery.'
124 if key_name.startswith(pref):
125 return key_name[len(pref):].replace('.', '_').lower()
126
127 def type_converter(parsed_key, value):
128 # cast to int
129 if value.isdigit():
130 return int(value)
131
132 # cast to bool
133 if value.lower() in ['true', 'false', 'True', 'False']:
134 return value.lower() == 'true'
135 return value
136
137 parser = configparser.SafeConfigParser(
138 defaults={'here': os.path.abspath(ini_location)})
139 parser.read(ini_location)
140
141 ini_config = {}
142 for k, v in parser.items('app:main'):
143 pref = 'celery.'
144 if k.startswith(pref):
145 ini_config[key_converter(k)] = type_converter(key_converter(k), v)
146
147 beat_config = {}
148 for section in parser.sections():
149 if section.startswith('celerybeat:'):
150 name = section.split(':', 1)[1]
151 beat_config[name] = get_beat_config(parser, section)
152
153 # final compose of settings
154 celery_settings = {}
155
156 if ini_config:
157 celery_settings.update(ini_config)
158 if beat_config:
159 celery_settings.update({'beat_schedule': beat_config})
160
161 return celery_settings
162
163
164 118 def parse_ini_vars(ini_vars):
165 119 options = {}
166 120 for pairs in ini_vars.split(','):
167 121 key, value = pairs.split('=')
168 122 options[key] = value
169 123 return options
170 124
171 125
172 126 def ping_db():
173 127 from rhodecode.model import meta
174 128 from rhodecode.model.db import DbMigrateVersion
175 129 log.info('Testing DB connection...')
176 130
177 131 for test in range(10):
178 132 try:
179 133 scalar = DbMigrateVersion.query().scalar()
180 134 log.debug('DB PING %s@%s', scalar, scalar.version)
181 135 break
182 136 except Exception:
183 137 retry = 1
184 138 log.debug('DB not ready, next try in %ss', retry)
185 139 time.sleep(retry)
186 140 finally:
187 141 meta.Session.remove()
@@ -1,203 +1,205 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import pytest
23 23
24 24 from rhodecode.tests import no_newline_id_generator
25 from rhodecode.config.middleware import (
26 _sanitize_vcs_settings, _bool_setting, _string_setting, _list_setting,
27 _int_setting)
25 from rhodecode.config.middleware import sanitize_settings_and_apply_defaults
26 from rhodecode.config.settings_maker import SettingsMaker
28 27
29 28
30 29 class TestHelperFunctions(object):
31 30 @pytest.mark.parametrize('raw, expected', [
32 31 ('true', True), (u'true', True),
33 32 ('yes', True), (u'yes', True),
34 33 ('on', True), (u'on', True),
35 34 ('false', False), (u'false', False),
36 35 ('no', False), (u'no', False),
37 36 ('off', False), (u'off', False),
38 37 ('invalid-bool-value', False),
39 38 ('invalid-∫øø@-√Γ₯@¨€', False),
40 39 (u'invalid-∫øø@-√Γ₯@¨€', False),
41 40 ])
42 def test_bool_setting_helper(self, raw, expected):
43 key = 'dummy-key'
44 settings = {key: raw}
45 _bool_setting(settings, key, None)
46 assert settings[key] is expected
41 def test_bool_func_helper(self, raw, expected):
42 val = SettingsMaker._bool_func(raw)
43 assert val == expected
47 44
48 45 @pytest.mark.parametrize('raw, expected', [
49 46 ('', ''),
50 47 ('test-string', 'test-string'),
51 48 ('CaSe-TeSt', 'case-test'),
52 49 ('test-string-烩€', 'test-string-烩€'),
53 50 (u'test-string-烩€', u'test-string-烩€'),
54 51 ])
55 def test_string_setting_helper(self, raw, expected):
56 key = 'dummy-key'
57 settings = {key: raw}
58 _string_setting(settings, key, None)
59 assert settings[key] == expected
52 def test_string_func_helper(self, raw, expected):
53 val = SettingsMaker._string_func(raw)
54 assert val == expected
60 55
61 56 @pytest.mark.parametrize('raw, expected', [
62 57 ('', []),
63 58 ('test', ['test']),
64 59 ('CaSe-TeSt', ['CaSe-TeSt']),
65 60 ('test-string-烩€', ['test-string-烩€']),
66 61 (u'test-string-烩€', [u'test-string-烩€']),
67 ('hg git svn', ['hg', 'git', 'svn']),
68 62 ('hg,git,svn', ['hg', 'git', 'svn']),
69 63 ('hg, git, svn', ['hg', 'git', 'svn']),
70 ('hg\ngit\nsvn', ['hg', 'git', 'svn']),
71 (' hg\n git\n svn ', ['hg', 'git', 'svn']),
64
72 65 (', hg , git , svn , ', ['', 'hg', 'git', 'svn', '']),
73 66 ('cheese,free node,other', ['cheese', 'free node', 'other']),
74 67 ], ids=no_newline_id_generator)
75 68 def test_list_setting_helper(self, raw, expected):
76 key = 'dummy-key'
77 settings = {key: raw}
78 _list_setting(settings, key, None)
79 assert settings[key] == expected
69 val = SettingsMaker._list_func(raw)
70 assert val == expected
71
72 @pytest.mark.parametrize('raw, expected', [
73 ('hg git svn', ['hg', 'git', 'svn']),
74 ], ids=no_newline_id_generator)
75 def test_list_setting_spaces_helper(self, raw, expected):
76 val = SettingsMaker._list_func(raw, sep=' ')
77 assert val == expected
78
79 @pytest.mark.parametrize('raw, expected', [
80 ('hg\ngit\nsvn', ['hg', 'git', 'svn']),
81 (' hg\n git\n svn ', ['hg', 'git', 'svn']),
82 ], ids=no_newline_id_generator)
83 def test_list_setting_newlines_helper(self, raw, expected):
84 val = SettingsMaker._list_func(raw, sep='\n')
85 assert val == expected
80 86
81 87 @pytest.mark.parametrize('raw, expected', [
82 88 ('0', 0),
83 89 ('-0', 0),
84 90 ('12345', 12345),
85 91 ('-12345', -12345),
86 92 (u'-12345', -12345),
87 93 ])
88 94 def test_int_setting_helper(self, raw, expected):
89 key = 'dummy-key'
90 settings = {key: raw}
91 _int_setting(settings, key, None)
92 assert settings[key] == expected
95 val = SettingsMaker._int_func(raw)
96 assert val == expected
93 97
94 98 @pytest.mark.parametrize('raw', [
95 99 ('0xff'),
96 100 (''),
97 101 ('invalid-int'),
98 102 ('invalid-⁄~†'),
99 103 (u'invalid-⁄~†'),
100 104 ])
101 105 def test_int_setting_helper_invalid_input(self, raw):
102 key = 'dummy-key'
103 settings = {key: raw}
104 106 with pytest.raises(Exception):
105 _int_setting(settings, key, None)
107 SettingsMaker._int_func(raw)
106 108
107 109
108 110 class TestSanitizeVcsSettings(object):
109 _bool_settings = [
111 _bool_funcs = [
110 112 ('vcs.hooks.direct_calls', False),
111 113 ('vcs.server.enable', True),
112 114 ('vcs.start_server', False),
113 115 ('startup.import_repos', False),
114 116 ]
115 117
116 _string_settings = [
118 _string_funcs = [
117 119 ('vcs.svn.compatible_version', ''),
118 120 ('vcs.hooks.protocol', 'http'),
119 121 ('vcs.hooks.host', '127.0.0.1'),
120 122 ('vcs.scm_app_implementation', 'http'),
121 123 ('vcs.server', ''),
122 124 ('vcs.server.protocol', 'http'),
123 125 ]
124 126
125 127 _list_settings = [
126 128 ('vcs.backends', 'hg git'),
127 129 ]
128 130
129 @pytest.mark.parametrize('key, default', _list_settings)
130 def test_list_setting_spacesep_list(self, key, default):
131 test_list = ['test', 'list', 'values', 'for', key]
132 input_value = ' '.join(test_list)
133 settings = {key: input_value}
134 _sanitize_vcs_settings(settings)
135 assert settings[key] == test_list
136
137 @pytest.mark.parametrize('key, default', _list_settings)
138 def test_list_setting_newlinesep_list(self, key, default):
139 test_list = ['test', 'list', 'values', 'for', key]
140 input_value = '\n'.join(test_list)
141 settings = {key: input_value}
142 _sanitize_vcs_settings(settings)
143 assert settings[key] == test_list
131 # @pytest.mark.parametrize('key, default', _list_settings)
132 # def test_list_setting_spacesep_list(self, key, default):
133 # test_list = ['test', 'list', 'values', 'for', key]
134 # input_value = ' '.join(test_list)
135 # settings = {key: input_value}
136 # sanitize_settings_and_apply_defaults({'__file__': ''}, settings)
137 # assert settings[key] == test_list
138 #
139 # @pytest.mark.parametrize('key, default', _list_settings)
140 # def test_list_setting_newlinesep_list(self, key, default):
141 # test_list = ['test', 'list', 'values', 'for', key]
142 # input_value = '\n'.join(test_list)
143 # settings = {key: input_value}
144 # sanitize_settings_and_apply_defaults({'__file__': ''}, settings)
145 # assert settings[key] == test_list
144 146
145 147 @pytest.mark.parametrize('key, default', _list_settings)
146 148 def test_list_setting_commasep_list(self, key, default):
147 149 test_list = ['test', 'list', 'values', 'for', key]
148 150 input_value = ','.join(test_list)
149 151 settings = {key: input_value}
150 _sanitize_vcs_settings(settings)
152 sanitize_settings_and_apply_defaults({'__file__': ''}, settings)
151 153 assert settings[key] == test_list
152 154
153 155 @pytest.mark.parametrize('key, default', _list_settings)
154 156 def test_list_setting_comma_and_space_sep_list(self, key, default):
155 157 test_list = ['test', 'list', 'values', 'for', key]
156 158 input_value = ', '.join(test_list)
157 159 settings = {key: input_value}
158 _sanitize_vcs_settings(settings)
160 sanitize_settings_and_apply_defaults({'__file__': ''}, settings)
159 161 assert settings[key] == test_list
160 162
161 @pytest.mark.parametrize('key, default', _string_settings)
162 def test_string_setting_string(self, key, default):
163 @pytest.mark.parametrize('key, default', _string_funcs)
164 def test_string_func_string(self, key, default):
163 165 test_value = 'test-string-for-{}'.format(key)
164 166 settings = {key: test_value}
165 _sanitize_vcs_settings(settings)
167 sanitize_settings_and_apply_defaults({'__file__': ''}, settings)
166 168 assert settings[key] == test_value
167 169
168 @pytest.mark.parametrize('key, default', _string_settings)
169 def test_string_setting_default(self, key, default):
170 @pytest.mark.parametrize('key, default', _string_funcs)
171 def test_string_func_default(self, key, default):
170 172 settings = {}
171 _sanitize_vcs_settings(settings)
173 sanitize_settings_and_apply_defaults({'__file__': ''}, settings)
172 174 assert settings[key] == default
173 175
174 @pytest.mark.parametrize('key, default', _string_settings)
175 def test_string_setting_lowercase(self, key, default):
176 test_value = 'Test-String-For-{}'.format(key)
177 settings = {key: test_value}
178 _sanitize_vcs_settings(settings)
179 assert settings[key] == test_value.lower()
176 # @pytest.mark.parametrize('key, default', _string_funcs)
177 # def test_string_func_lowercase(self, key, default):
178 # test_value = 'Test-String-For-{}'.format(key)
179 # settings = {key: test_value}
180 # sanitize_settings_and_apply_defaults({'__file__': ''}, settings)
181 # assert settings[key] == test_value.lower()
180 182
181 @pytest.mark.parametrize('key, default', _bool_settings)
182 def test_bool_setting_true(self, key, default):
183 @pytest.mark.parametrize('key, default', _bool_funcs)
184 def test_bool_func_true(self, key, default):
183 185 settings = {key: 'true'}
184 _sanitize_vcs_settings(settings)
186 sanitize_settings_and_apply_defaults({'__file__': ''}, settings)
185 187 assert settings[key] is True
186 188
187 @pytest.mark.parametrize('key, default', _bool_settings)
188 def test_bool_setting_false(self, key, default):
189 @pytest.mark.parametrize('key, default', _bool_funcs)
190 def test_bool_func_false(self, key, default):
189 191 settings = {key: 'false'}
190 _sanitize_vcs_settings(settings)
192 sanitize_settings_and_apply_defaults({'__file__': ''}, settings)
191 193 assert settings[key] is False
192 194
193 @pytest.mark.parametrize('key, default', _bool_settings)
194 def test_bool_setting_invalid_string(self, key, default):
195 @pytest.mark.parametrize('key, default', _bool_funcs)
196 def test_bool_func_invalid_string(self, key, default):
195 197 settings = {key: 'no-bool-val-string'}
196 _sanitize_vcs_settings(settings)
198 sanitize_settings_and_apply_defaults({'__file__': ''}, settings)
197 199 assert settings[key] is False
198 200
199 @pytest.mark.parametrize('key, default', _bool_settings)
200 def test_bool_setting_default(self, key, default):
201 @pytest.mark.parametrize('key, default', _bool_funcs)
202 def test_bool_func_default(self, key, default):
201 203 settings = {}
202 _sanitize_vcs_settings(settings)
204 sanitize_settings_and_apply_defaults({'__file__': ''}, settings)
203 205 assert settings[key] is default
This diff has been collapsed as it changes many lines, (568 lines changed) Show them Hide them
@@ -1,664 +1,658 b''
1 1
2
3 ################################################################################
4 ## RHODECODE COMMUNITY EDITION CONFIGURATION ##
5 # The %(here)s variable will be replaced with the parent directory of this file#
6 ################################################################################
2 ; #########################################
3 ; RHODECODE COMMUNITY EDITION CONFIGURATION
4 ; #########################################
7 5
8 6 [DEFAULT]
7 ; Debug flag sets all loggers to debug, and enables request tracking
9 8 debug = true
10 9
11 ################################################################################
12 ## EMAIL CONFIGURATION ##
13 ## Uncomment and replace with the email address which should receive ##
14 ## any error reports after an application crash ##
15 ## Additionally these settings will be used by the RhodeCode mailing system ##
16 ################################################################################
10 ; ########################################################################
11 ; EMAIL CONFIGURATION
12 ; These settings will be used by the RhodeCode mailing system
13 ; ########################################################################
17 14
18 ## prefix all emails subjects with given prefix, helps filtering out emails
15 ; prefix all emails subjects with given prefix, helps filtering out emails
19 16 #email_prefix = [RhodeCode]
20 17
21 ## email FROM address all mails will be sent
18 ; email FROM address all mails will be sent
22 19 #app_email_from = rhodecode-noreply@localhost
23 20
24 ## Uncomment and replace with the address which should receive any error report
25 ## note: using appenlight for error handling doesn't need this to be uncommented
26 #email_to = admin@localhost
27
28 21 #smtp_server = mail.server.com
29 22 #smtp_username =
30 23 #smtp_password =
31 24 #smtp_port =
32 25 #smtp_use_tls = false
33 26 #smtp_use_ssl = true
34 27
35 28 [server:main]
36 ## COMMON ##
29 ; COMMON HOST/IP CONFIG
37 30 host = 0.0.0.0
38 31 port = 5000
39 32
40 ##########################
41 ## GUNICORN WSGI SERVER ##
42 ##########################
43 ## run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
33
34 ; ###########################
35 ; GUNICORN APPLICATION SERVER
36 ; ###########################
44 37
38 ; run with gunicorn --log-config rhodecode.ini --paste rhodecode.ini
39
40 ; Module to use, this setting shouldn't be changed
45 41 use = egg:gunicorn#main
42
46 43 ## Sets the number of process workers. You must set `instance_id = *`
47 44 ## when this option is set to more than one worker, recommended
48 45 ## value is (2 * NUMBER_OF_CPUS + 1), eg 2CPU = 5 workers
49 46 ## The `instance_id = *` must be set in the [app:main] section below
50 47 #workers = 2
51 48 ## number of threads for each of the worker, must be set to 1 for gevent
52 49 ## generally recommened to be at 1
53 50 #threads = 1
54 51 ## process name
55 52 #proc_name = rhodecode
56 53 ## type of worker class, one of sync, gevent
57 54 ## recommended for bigger setup is using of of other than sync one
58 55 #worker_class = sync
59 56 ## The maximum number of simultaneous clients. Valid only for Gevent
60 57 #worker_connections = 10
61 58 ## max number of requests that worker will handle before being gracefully
62 59 ## restarted, could prevent memory leaks
63 60 #max_requests = 1000
64 61 #max_requests_jitter = 30
65 62 ## amount of time a worker can spend with handling a request before it
66 63 ## gets killed and restarted. Set to 6hrs
67 64 #timeout = 21600
68 65
69 66 ## prefix middleware for RhodeCode.
70 67 ## recommended when using proxy setup.
71 68 ## allows to set RhodeCode under a prefix in server.
72 69 ## eg https://server.com/custom_prefix. Enable `filter-with =` option below as well.
73 70 ## And set your prefix like: `prefix = /custom_prefix`
74 71 ## be sure to also set beaker.session.cookie_path = /custom_prefix if you need
75 72 ## to make your cookies only work on prefix url
76 73 [filter:proxy-prefix]
77 74 use = egg:PasteDeploy#prefix
78 75 prefix = /
79 76
80 77 [app:main]
81 78 is_test = True
82 79 use = egg:rhodecode-enterprise-ce
83 80
84 ## enable proxy prefix middleware, defined above
81 ; enable proxy prefix middleware, defined above
85 82 #filter-with = proxy-prefix
86 83
87 84
88 85 ## RHODECODE PLUGINS ##
89 86 rhodecode.includes = rhodecode.api
90 87
91 88 # api prefix url
92 89 rhodecode.api.url = /_admin/api
93 90
94 91
95 92 ## END RHODECODE PLUGINS ##
96 93
97 94 ## encryption key used to encrypt social plugin tokens,
98 95 ## remote_urls with credentials etc, if not set it defaults to
99 96 ## `beaker.session.secret`
100 97 #rhodecode.encrypted_values.secret =
101 98
102 ## decryption strict mode (enabled by default). It controls if decryption raises
103 ## `SignatureVerificationError` in case of wrong key, or damaged encryption data.
99 ; decryption strict mode (enabled by default). It controls if decryption raises
100 ; `SignatureVerificationError` in case of wrong key, or damaged encryption data.
104 101 #rhodecode.encrypted_values.strict = false
105 102
106 ## return gzipped responses from Rhodecode (static files/application)
103 ; Pick algorithm for encryption. Either fernet (more secure) or aes (default)
104 ; fernet is safer, and we strongly recommend switching to it.
105 ; Due to backward compatibility aes is used as default.
106 #rhodecode.encrypted_values.algorithm = fernet
107
108 ; Return gzipped responses from RhodeCode (static files/application)
107 109 gzip_responses = false
108 110
109 ## autogenerate javascript routes file on startup
111 ; Auto-generate javascript routes file on startup
110 112 generate_js_files = false
111 113
112 ## Optional Languages
113 ## en(default), be, de, es, fr, it, ja, pl, pt, ru, zh
114 ; System global default language.
115 ; All available languages: en (default), be, de, es, fr, it, ja, pl, pt, ru, zh
114 116 lang = en
115 117
116 118 ## perform a full repository scan on each server start, this should be
117 119 ## set to false after first startup, to allow faster server restarts.
118 120 startup.import_repos = true
119 121
120 ## Uncomment and set this path to use archive download cache.
121 ## Once enabled, generated archives will be cached at this location
122 ## and served from the cache during subsequent requests for the same archive of
123 ## the repository.
122 ; Uncomment and set this path to use archive download cache.
123 ; Once enabled, generated archives will be cached at this location
124 ; and served from the cache during subsequent requests for the same archive of
125 ; the repository.
124 126 #archive_cache_dir = /tmp/tarballcache
125 127
126 ## URL at which the application is running. This is used for bootstraping
127 ## requests in context when no web request is available. Used in ishell, or
128 ## SSH calls. Set this for events to receive proper url for SSH calls.
128 ; URL at which the application is running. This is used for Bootstrapping
129 ; requests in context when no web request is available. Used in ishell, or
130 ; SSH calls. Set this for events to receive proper url for SSH calls.
129 131 app.base_url = http://rhodecode.local
130 132
131 ## change this to unique ID for security
133 ; Unique application ID. Should be a random unique string for security.
132 134 app_instance_uuid = rc-production
133 135
134 136 ## cut off limit for large diffs (size in bytes)
135 137 cut_off_limit_diff = 1024000
136 138 cut_off_limit_file = 256000
137 139
138 ## use cache version of scm repo everywhere
140 ; Use cached version of vcs repositories everywhere. Recommended to be `true`
139 141 vcs_full_cache = false
140 142
141 ## force https in RhodeCode, fixes https redirects, assumes it's always https
142 ## Normally this is controlled by proper http flags sent from http server
143 ; Force https in RhodeCode, fixes https redirects, assumes it's always https.
144 ; Normally this is controlled by proper flags sent from http server such as Nginx or Apache
143 145 force_https = false
144 146
145 ## use Strict-Transport-Security headers
147 ; use Strict-Transport-Security headers
146 148 use_htsts = false
147 149
148 # Set to true if your repos are exposed using the dumb protocol
150 ; Set to true if your repos are exposed using the dumb protocol
149 151 git_update_server_info = false
150 152
151 ## RSS/ATOM feed options
153 ; RSS/ATOM feed options
152 154 rss_cut_off_limit = 256000
153 155 rss_items_per_page = 10
154 156 rss_include_diff = false
155 157
156 ## gist URL alias, used to create nicer urls for gist. This should be an
157 ## url that does rewrites to _admin/gists/{gistid}.
158 ## example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
159 ## RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
158 ; gist URL alias, used to create nicer urls for gist. This should be an
159 ; url that does rewrites to _admin/gists/{gistid}.
160 ; example: http://gist.rhodecode.org/{gistid}. Empty means use the internal
161 ; RhodeCode url, ie. http[s]://rhodecode.server/_admin/gists/{gistid}
160 162 gist_alias_url =
161 163
162 164 ## List of views (using glob pattern syntax) that AUTH TOKENS could be
163 165 ## used for access.
164 166 ## Adding ?auth_token=TOKEN_HASH to the url authenticates this request as if it
165 167 ## came from the the logged in user who own this authentication token.
166 168 ## Additionally @TOKEN syntaxt can be used to bound the view to specific
167 169 ## authentication token. Such view would be only accessible when used together
168 170 ## with this authentication token
169 171 ##
170 172 ## list of all views can be found under `/_admin/permissions/auth_token_access`
171 173 ## The list should be "," separated and on a single line.
172 174 ##
173 175 ## Most common views to enable:
174 176 # RepoCommitsView:repo_commit_download
175 177 # RepoCommitsView:repo_commit_patch
176 178 # RepoCommitsView:repo_commit_raw
177 179 # RepoCommitsView:repo_commit_raw@TOKEN
178 180 # RepoFilesView:repo_files_diff
179 181 # RepoFilesView:repo_archivefile
180 182 # RepoFilesView:repo_file_raw
181 183 # GistView:*
182 184 api_access_controllers_whitelist =
183 185
184 ## default encoding used to convert from and to unicode
185 ## can be also a comma separated list of encoding in case of mixed encodings
186 ; Default encoding used to convert from and to unicode
187 ; can be also a comma separated list of encoding in case of mixed encodings
186 188 default_encoding = UTF-8
187 189
188 ## instance-id prefix
189 ## a prefix key for this instance used for cache invalidation when running
190 ## multiple instances of rhodecode, make sure it's globally unique for
191 ## all running rhodecode instances. Leave empty if you don't use it
190 ; instance-id prefix
191 ; a prefix key for this instance used for cache invalidation when running
192 ; multiple instances of RhodeCode, make sure it's globally unique for
193 ; all running RhodeCode instances. Leave empty if you don't use it
192 194 instance_id =
193 195
194 ## Fallback authentication plugin. Set this to a plugin ID to force the usage
195 ## of an authentication plugin also if it is disabled by it's settings.
196 ## This could be useful if you are unable to log in to the system due to broken
197 ## authentication settings. Then you can enable e.g. the internal rhodecode auth
198 ## module to log in again and fix the settings.
199 ##
200 ## Available builtin plugin IDs (hash is part of the ID):
201 ## egg:rhodecode-enterprise-ce#rhodecode
202 ## egg:rhodecode-enterprise-ce#pam
203 ## egg:rhodecode-enterprise-ce#ldap
204 ## egg:rhodecode-enterprise-ce#jasig_cas
205 ## egg:rhodecode-enterprise-ce#headers
206 ## egg:rhodecode-enterprise-ce#crowd
196 ; Fallback authentication plugin. Set this to a plugin ID to force the usage
197 ; of an authentication plugin also if it is disabled by it's settings.
198 ; This could be useful if you are unable to log in to the system due to broken
199 ; authentication settings. Then you can enable e.g. the internal RhodeCode auth
200 ; module to log in again and fix the settings.
201 ; Available builtin plugin IDs (hash is part of the ID):
202 ; egg:rhodecode-enterprise-ce#rhodecode
203 ; egg:rhodecode-enterprise-ce#pam
204 ; egg:rhodecode-enterprise-ce#ldap
205 ; egg:rhodecode-enterprise-ce#jasig_cas
206 ; egg:rhodecode-enterprise-ce#headers
207 ; egg:rhodecode-enterprise-ce#crowd
208
207 209 #rhodecode.auth_plugin_fallback = egg:rhodecode-enterprise-ce#rhodecode
208 210
209 ## alternative return HTTP header for failed authentication. Default HTTP
210 ## response is 401 HTTPUnauthorized. Currently HG clients have troubles with
211 ## handling that causing a series of failed authentication calls.
212 ## Set this variable to 403 to return HTTPForbidden, or any other HTTP code
213 ## This will be served instead of default 401 on bad authnetication
211 ; Flag to control loading of legacy plugins in py:/path format
212 auth_plugin.import_legacy_plugins = true
213
214 ; alternative return HTTP header for failed authentication. Default HTTP
215 ; response is 401 HTTPUnauthorized. Currently HG clients have troubles with
216 ; handling that causing a series of failed authentication calls.
217 ; Set this variable to 403 to return HTTPForbidden, or any other HTTP code
218 ; This will be served instead of default 401 on bad authentication
214 219 auth_ret_code =
215 220
216 221 ## use special detection method when serving auth_ret_code, instead of serving
217 222 ## ret_code directly, use 401 initially (Which triggers credentials prompt)
218 223 ## and then serve auth_ret_code to clients
219 224 auth_ret_code_detection = false
220 225
221 226 ## locking return code. When repository is locked return this HTTP code. 2XX
222 227 ## codes don't break the transactions while 4XX codes do
223 228 lock_ret_code = 423
224 229
225 230 ## allows to change the repository location in settings page
226 231 allow_repo_location_change = true
227 232
228 233 ## allows to setup custom hooks in settings page
229 234 allow_custom_hooks_settings = true
230 235
231 236 ## generated license token, goto license page in RhodeCode settings to obtain
232 237 ## new token
233 238 license_token = abra-cada-bra1-rce3
234 239
235 240 ## supervisor connection uri, for managing supervisor and logs.
236 241 supervisor.uri =
237 242 ## supervisord group name/id we only want this RC instance to handle
238 243 supervisor.group_id = dev
239 244
240 245 ## Display extended labs settings
241 246 labs_settings_active = true
242 247
243 ####################################
244 ### CELERY CONFIG ####
245 ####################################
248 ; #############
249 ; CELERY CONFIG
250 ; #############
251
252 ; manually run celery: /path/to/celery worker -E --beat --app rhodecode.lib.celerylib.loader --scheduler rhodecode.lib.celerylib.scheduler.RcScheduler --loglevel DEBUG --ini /path/to/rhodecode.ini
253
246 254 use_celery = false
247 broker.host = localhost
248 broker.vhost = rabbitmqhost
249 broker.port = 5672
250 broker.user = rabbitmq
251 broker.password = qweqwe
252 255
253 celery.imports = rhodecode.lib.celerylib.tasks
256 ; path to store schedule database
257 #celerybeat-schedule.path =
254 258
255 celery.result.backend = amqp
256 celery.result.dburi = amqp://
257 celery.result.serialier = json
259 ; connection url to the message broker (default redis)
260 celery.broker_url = redis://localhost:6379/8
258 261
259 #celery.send.task.error.emails = true
260 #celery.amqp.task.result.expires = 18000
262 ; rabbitmq example
263 #celery.broker_url = amqp://rabbitmq:qweqwe@localhost:5672/rabbitmqhost
261 264
262 celeryd.concurrency = 2
263 #celeryd.log.file = celeryd.log
264 celeryd.log.level = debug
265 celeryd.max.tasks.per.child = 1
265 ; maximum tasks to execute before worker restart
266 celery.max_tasks_per_child = 100
266 267
267 ## tasks will never be sent to the queue, but executed locally instead.
268 celery.always.eager = false
268 ; tasks will never be sent to the queue, but executed locally instead.
269 celery.task_always_eager = false
269 270
270 ####################################
271 ### BEAKER CACHE ####
272 ####################################
273 # default cache dir for templates. Putting this into a ramdisk
274 ## can boost performance, eg. %(here)s/data_ramdisk
271 ; #############
272 ; DOGPILE CACHE
273 ; #############
274
275 ; Default cache dir for caches. Putting this into a ramdisk can boost performance.
276 ; eg. /tmpfs/data_ramdisk, however this directory might require large amount of space
275 277 cache_dir = %(here)s/data
276 278
277 279 ## locking and default file storage for Beaker. Putting this into a ramdisk
278 280 ## can boost performance, eg. %(here)s/data_ramdisk/cache/beaker_data
279 281 beaker.cache.data_dir = %(here)s/rc/data/cache/beaker_data
280 282 beaker.cache.lock_dir = %(here)s/rc/data/cache/beaker_lock
281 283
282 284 beaker.cache.regions = long_term
283 285
284 286 beaker.cache.long_term.type = memory
285 287 beaker.cache.long_term.expire = 36000
286 288 beaker.cache.long_term.key_length = 256
287 289
288 290
289 291 #####################################
290 292 ### DOGPILE CACHE ####
291 293 #####################################
292 294
293 295 ## permission tree cache settings
294 296 rc_cache.cache_perms.backend = dogpile.cache.rc.file_namespace
295 297 rc_cache.cache_perms.expiration_time = 0
296 298 rc_cache.cache_perms.arguments.filename = /tmp/rc_cache_1
297 299
298 300
299 301 ## cache settings for SQL queries
300 302 rc_cache.sql_cache_short.backend = dogpile.cache.rc.memory_lru
301 303 rc_cache.sql_cache_short.expiration_time = 0
302 304
303 305
304 ####################################
305 ### BEAKER SESSION ####
306 ####################################
306 ; ##############
307 ; BEAKER SESSION
308 ; ##############
307 309
308 ## .session.type is type of storage options for the session, current allowed
309 ## types are file, ext:memcached, ext:database, and memory (default).
310 ; beaker.session.type is type of storage options for the logged users sessions. Current allowed
311 ; types are file, ext:redis, ext:database, ext:memcached, and memory (default if not specified).
312 ; Fastest ones are Redis and ext:database
310 313 beaker.session.type = file
311 314 beaker.session.data_dir = %(here)s/rc/data/sessions/data
312 315
313 ## db based session, fast, and allows easy management over logged in users
316 ; Redis based sessions
317 #beaker.session.type = ext:redis
318 #beaker.session.url = redis://127.0.0.1:6379/2
319
320 ; DB based session, fast, and allows easy management over logged in users
314 321 #beaker.session.type = ext:database
315 322 #beaker.session.table_name = db_session
316 323 #beaker.session.sa.url = postgresql://postgres:secret@localhost/rhodecode
317 324 #beaker.session.sa.url = mysql://root:secret@127.0.0.1/rhodecode
318 325 #beaker.session.sa.pool_recycle = 3600
319 326 #beaker.session.sa.echo = false
320 327
321 328 beaker.session.key = rhodecode
322 329 beaker.session.secret = test-rc-uytcxaz
323 330 beaker.session.lock_dir = %(here)s/rc/data/sessions/lock
324 331
325 ## Secure encrypted cookie. Requires AES and AES python libraries
326 ## you must disable beaker.session.secret to use this
332 ; Secure encrypted cookie. Requires AES and AES python libraries
333 ; you must disable beaker.session.secret to use this
327 334 #beaker.session.encrypt_key = key_for_encryption
328 335 #beaker.session.validate_key = validation_key
329 336
330 ## sets session as invalid(also logging out user) if it haven not been
331 ## accessed for given amount of time in seconds
337 ; Sets session as invalid (also logging out user) if it haven not been
338 ; accessed for given amount of time in seconds
332 339 beaker.session.timeout = 2592000
333 340 beaker.session.httponly = true
334 ## Path to use for the cookie. Set to prefix if you use prefix middleware
341
342 ; Path to use for the cookie. Set to prefix if you use prefix middleware
335 343 #beaker.session.cookie_path = /custom_prefix
336 344
337 ## uncomment for https secure cookie
345 ; Set https secure cookie
338 346 beaker.session.secure = false
339 347
340 348 ## auto save the session to not to use .save()
341 349 beaker.session.auto = false
342 350
343 351 ## default cookie expiration time in seconds, set to `true` to set expire
344 352 ## at browser close
345 353 #beaker.session.cookie_expires = 3600
346 354
347 ###################################
348 ## SEARCH INDEXING CONFIGURATION ##
349 ###################################
350 ## Full text search indexer is available in rhodecode-tools under
351 ## `rhodecode-tools index` command
355 ; #############################
356 ; SEARCH INDEXING CONFIGURATION
357 ; #############################
352 358
353 359 ## WHOOSH Backend, doesn't require additional services to run
354 360 ## it works good with few dozen repos
355 361 search.module = rhodecode.lib.index.whoosh
356 362 search.location = %(here)s/data/index
357 363
358 ########################################
359 ### CHANNELSTREAM CONFIG ####
360 ########################################
361 ## channelstream enables persistent connections and live notification
362 ## in the system. It's also used by the chat system
364 ; ####################
365 ; CHANNELSTREAM CONFIG
366 ; ####################
367
368 ; channelstream enables persistent connections and live notification
369 ; in the system. It's also used by the chat system
363 370
364 371 channelstream.enabled = false
365 372
366 ## server address for channelstream server on the backend
373 ; server address for channelstream server on the backend
367 374 channelstream.server = 127.0.0.1:9800
368 ## location of the channelstream server from outside world
369 ## use ws:// for http or wss:// for https. This address needs to be handled
370 ## by external HTTP server such as Nginx or Apache
371 ## see nginx/apache configuration examples in our docs
375
376 ; location of the channelstream server from outside world
377 ; use ws:// for http or wss:// for https. This address needs to be handled
378 ; by external HTTP server such as Nginx or Apache
379 ; see Nginx/Apache configuration examples in our docs
372 380 channelstream.ws_url = ws://rhodecode.yourserver.com/_channelstream
373 381 channelstream.secret = secret
374 382 channelstream.history.location = %(here)s/channelstream_history
375 383
376 ## Internal application path that Javascript uses to connect into.
377 ## If you use proxy-prefix the prefix should be added before /_channelstream
384 ; Internal application path that Javascript uses to connect into.
385 ; If you use proxy-prefix the prefix should be added before /_channelstream
378 386 channelstream.proxy_path = /_channelstream
379 387
380 388
381 ###################################
382 ## APPENLIGHT CONFIG ##
383 ###################################
384
385 ## Appenlight is tailored to work with RhodeCode, see
386 ## http://appenlight.com for details how to obtain an account
387
388 ## appenlight integration enabled
389 appenlight = false
390
391 appenlight.server_url = https://api.appenlight.com
392 appenlight.api_key = YOUR_API_KEY
393 #appenlight.transport_config = https://api.appenlight.com?threaded=1&timeout=5
394
395 # used for JS client
396 appenlight.api_public_key = YOUR_API_PUBLIC_KEY
397
398 ## TWEAK AMOUNT OF INFO SENT HERE
399
400 ## enables 404 error logging (default False)
401 appenlight.report_404 = false
402
403 ## time in seconds after request is considered being slow (default 1)
404 appenlight.slow_request_time = 1
405
406 ## record slow requests in application
407 ## (needs to be enabled for slow datastore recording and time tracking)
408 appenlight.slow_requests = true
409
410 ## enable hooking to application loggers
411 appenlight.logging = true
412
413 ## minimum log level for log capture
414 appenlight.logging.level = WARNING
415
416 ## send logs only from erroneous/slow requests
417 ## (saves API quota for intensive logging)
418 appenlight.logging_on_error = false
389 ; ##############################
390 ; MAIN RHODECODE DATABASE CONFIG
391 ; ##############################
419 392
420 ## list of additonal keywords that should be grabbed from environ object
421 ## can be string with comma separated list of words in lowercase
422 ## (by default client will always send following info:
423 ## 'REMOTE_USER', 'REMOTE_ADDR', 'SERVER_NAME', 'CONTENT_TYPE' + all keys that
424 ## start with HTTP* this list be extended with additional keywords here
425 appenlight.environ_keys_whitelist =
426
427 ## list of keywords that should be blanked from request object
428 ## can be string with comma separated list of words in lowercase
429 ## (by default client will always blank keys that contain following words
430 ## 'password', 'passwd', 'pwd', 'auth_tkt', 'secret', 'csrf'
431 ## this list be extended with additional keywords set here
432 appenlight.request_keys_blacklist =
433
434 ## list of namespaces that should be ignores when gathering log entries
435 ## can be string with comma separated list of namespaces
436 ## (by default the client ignores own entries: appenlight_client.client)
437 appenlight.log_namespace_blacklist =
438
393 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db?timeout=30
394 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode
395 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode?charset=utf8
396 ; pymysql is an alternative driver for MySQL, use in case of problems with default one
397 #sqlalchemy.db1.url = mysql+pymysql://root:qweqwe@localhost/rhodecode
439 398
440 ################################################################################
441 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
442 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
443 ## execute malicious code after an exception is raised. ##
444 ################################################################################
445 set debug = false
446
447
448 ##############
449 ## STYLING ##
450 ##############
451 debug_style = false
452
453 ###########################################
454 ### MAIN RHODECODE DATABASE CONFIG ###
455 ###########################################
456 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode_test.db?timeout=30
457 #sqlalchemy.db1.url = postgresql://postgres:qweqwe@localhost/rhodecode_test
458 #sqlalchemy.db1.url = mysql://root:qweqwe@localhost/rhodecode_test
459 399 sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode_test.db?timeout=30
460 400
461 # see sqlalchemy docs for other advanced settings
462
463 ## print the sql statements to output
401 ; see sqlalchemy docs for other advanced settings
402 ; print the sql statements to output
464 403 sqlalchemy.db1.echo = false
465 ## recycle the connections after this amount of seconds
466 sqlalchemy.db1.pool_recycle = 3600
467 404
468 ## the number of connections to keep open inside the connection pool.
469 ## 0 indicates no limit
405 ; recycle the connections after this amount of seconds
406 sqlalchemy.db1.pool_recycle = 3600
407 sqlalchemy.db1.convert_unicode = true
408
409 ; the number of connections to keep open inside the connection pool.
410 ; 0 indicates no limit
470 411 #sqlalchemy.db1.pool_size = 5
471 412
472 ## the number of connections to allow in connection pool "overflow", that is
473 ## connections that can be opened above and beyond the pool_size setting,
474 ## which defaults to five.
413 ; The number of connections to allow in connection pool "overflow", that is
414 ; connections that can be opened above and beyond the pool_size setting,
415 ; which defaults to five.
475 416 #sqlalchemy.db1.max_overflow = 10
476 417
418 ; Connection check ping, used to detect broken database connections
419 ; could be enabled to better handle cases if MySQL has gone away errors
420 #sqlalchemy.db1.ping_connection = true
477 421
478 ##################
479 ### VCS CONFIG ###
480 ##################
422 ; ##########
423 ; VCS CONFIG
424 ; ##########
481 425 vcs.server.enable = true
482 426 vcs.server = localhost:9901
483 427
484 ## Web server connectivity protocol, responsible for web based VCS operatations
485 ## Available protocols are:
486 ## `http` - use http-rpc backend (default)
428 ; Web server connectivity protocol, responsible for web based VCS operations
429 ; Available protocols are:
430 ; `http` - use http-rpc backend (default)
487 431 vcs.server.protocol = http
488 432
489 ## Push/Pull operations protocol, available options are:
490 ## `http` - use http-rpc backend (default)
491 ## `vcsserver.scm_app` - internal app (EE only)
433 ; Push/Pull operations protocol, available options are:
434 ; `http` - use http-rpc backend (default)
492 435 vcs.scm_app_implementation = http
493 436
494 ## Push/Pull operations hooks protocol, available options are:
495 ## `http` - use http-rpc backend (default)
437 ; Push/Pull operations hooks protocol, available options are:
438 ; `http` - use http-rpc backend (default)
496 439 vcs.hooks.protocol = http
440
441 ; Host on which this instance is listening for hooks. If vcsserver is in other location
442 ; this should be adjusted.
497 443 vcs.hooks.host = 127.0.0.1
498 444
499
500 ## Start VCSServer with this instance as a subprocess, Useful for development
445 ; Start VCSServer with this instance as a subprocess, useful for development
501 446 vcs.start_server = false
502 447
503 ## List of enabled VCS backends, available options are:
504 ## `hg` - mercurial
505 ## `git` - git
506 ## `svn` - subversion
448 ; List of enabled VCS backends, available options are:
449 ; `hg` - mercurial
450 ; `git` - git
451 ; `svn` - subversion
507 452 vcs.backends = hg, git, svn
508 453
454 ; Wait this number of seconds before killing connection to the vcsserver
509 455 vcs.connection_timeout = 3600
510 ## Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
511 ## Available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
512 #vcs.svn.compatible_version = pre-1.8-compatible
513 456
457 ; Compatibility version when creating SVN repositories. Defaults to newest version when commented out.
458 ; Set a numeric version for your current SVN e.g 1.8, or 1.12
459 ; Legacy available options are: pre-1.4-compatible, pre-1.5-compatible, pre-1.6-compatible, pre-1.8-compatible, pre-1.9-compatible
460 #vcs.svn.compatible_version = 1.8
514 461
515 ############################################################
516 ### Subversion proxy support (mod_dav_svn) ###
517 ### Maps RhodeCode repo groups into SVN paths for Apache ###
518 ############################################################
519 ## Enable or disable the config file generation.
462 ; Cache flag to cache vcsserver remote calls locally
463 ; It uses cache_region `cache_repo`
464 vcs.methods.cache = false
465
466 ; ####################################################
467 ; Subversion proxy support (mod_dav_svn)
468 ; Maps RhodeCode repo groups into SVN paths for Apache
469 ; ####################################################
470
471 ; Enable or disable the config file generation.
520 472 svn.proxy.generate_config = false
521 ## Generate config file with `SVNListParentPath` set to `On`.
473
474 ; Generate config file with `SVNListParentPath` set to `On`.
522 475 svn.proxy.list_parent_path = true
523 ## Set location and file name of generated config file.
476
477 ; Set location and file name of generated config file.
524 478 svn.proxy.config_file_path = %(here)s/mod_dav_svn.conf
525 ## Used as a prefix to the `Location` block in the generated config file.
526 ## In most cases it should be set to `/`.
479
480 ; alternative mod_dav config template. This needs to be a valid mako template
481 ; Example template can be found in the source code:
482 ; rhodecode/apps/svn_support/templates/mod-dav-svn.conf.mako
483 #svn.proxy.config_template = ~/.rccontrol/enterprise-1/custom_svn_conf.mako
484
485 ; Used as a prefix to the `Location` block in the generated config file.
486 ; In most cases it should be set to `/`.
527 487 svn.proxy.location_root = /
528 ## Command to reload the mod dav svn configuration on change.
529 ## Example: `/etc/init.d/apache2 reload`
488
489 ; Command to reload the mod dav svn configuration on change.
490 ; Example: `/etc/init.d/apache2 reload` or /home/USER/apache_reload.sh
491 ; Make sure user who runs RhodeCode process is allowed to reload Apache
530 492 #svn.proxy.reload_cmd = /etc/init.d/apache2 reload
531 ## If the timeout expires before the reload command finishes, the command will
532 ## be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
493
494 ; If the timeout expires before the reload command finishes, the command will
495 ; be killed. Setting it to zero means no timeout. Defaults to 10 seconds.
533 496 #svn.proxy.reload_timeout = 10
534 497
535 ############################################################
536 ### SSH Support Settings ###
537 ############################################################
498 ; ####################
499 ; SSH Support Settings
500 ; ####################
538 501
539 ## Defines if the authorized_keys file should be written on any change of
540 ## user ssh keys, setting this to false also disables posibility of adding
541 ## ssh keys for users from web interface.
502 ; Defines if a custom authorized_keys file should be created and written on
503 ; any change user ssh keys. Setting this to false also disables possibility
504 ; of adding SSH keys by users from web interface. Super admins can still
505 ; manage SSH Keys.
542 506 ssh.generate_authorized_keyfile = true
543 507
544 ## Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
508 ; Options for ssh, default is `no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding`
545 509 # ssh.authorized_keys_ssh_opts =
546 510
547 ## File to generate the authorized keys together with options
548 ## It is possible to have multiple key files specified in `sshd_config` e.g.
549 ## AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
511 ; Path to the authorized_keys file where the generate entries are placed.
512 ; It is possible to have multiple key files specified in `sshd_config` e.g.
513 ; AuthorizedKeysFile %h/.ssh/authorized_keys %h/.ssh/authorized_keys_rhodecode
550 514 ssh.authorized_keys_file_path = %(here)s/rc/authorized_keys_rhodecode
551 515
552 ## Command to execute the SSH wrapper. The binary is available in the
553 ## rhodecode installation directory.
554 ## e.g ~/.rccontrol/community-1/profile/bin/rc-ssh-wrapper
516 ; Command to execute the SSH wrapper. The binary is available in the
517 ; RhodeCode installation directory.
518 ; e.g ~/.rccontrol/community-1/profile/bin/rc-ssh-wrapper
555 519 ssh.wrapper_cmd = ~/.rccontrol/community-1/rc-ssh-wrapper
556 520
557 ## Allow shell when executing the ssh-wrapper command
521 ; Allow shell when executing the ssh-wrapper command
558 522 ssh.wrapper_cmd_allow_shell = false
559 523
560 ## Enables logging, and detailed output send back to the client. Useful for
561 ## debugging, shouldn't be used in production.
524 ; Enables logging, and detailed output send back to the client during SSH
525 ; operations. Useful for debugging, shouldn't be used in production.
562 526 ssh.enable_debug_logging = false
563 527
564 ## Paths to binary executrables, by default they are the names, but we can
565 ## override them if we want to use a custom one
528 ; Paths to binary executable, by default they are the names, but we can
529 ; override them if we want to use a custom one
566 530 ssh.executable.hg = ~/.rccontrol/vcsserver-1/profile/bin/hg
567 531 ssh.executable.git = ~/.rccontrol/vcsserver-1/profile/bin/git
568 532 ssh.executable.svn = ~/.rccontrol/vcsserver-1/profile/bin/svnserve
569 533
570 ## Enables SSH key generator web interface. Disabling this still allows users
571 ## to add their own keys.
534 ; Enables SSH key generator web interface. Disabling this still allows users
535 ; to add their own keys.
572 536 ssh.enable_ui_key_generator = true
573 537
538 ; Statsd client config, this is used to send metrics to statsd
539 ; We recommend setting statsd_exported and scrape them using Promethues
540 #statsd.enabled = false
541 #statsd.statsd_host = 0.0.0.0
542 #statsd.statsd_port = 8125
543 #statsd.statsd_prefix =
544 #statsd.statsd_ipv6 = false
574 545
575 ## Dummy marker to add new entries after.
576 ## Add any custom entries below. Please don't remove.
546
547 ; configure logging automatically at server startup set to false
548 ; to use the below custom logging config.
549 logging.autoconfigure = false
550
551 ; specify your own custom logging config file to configure logging
552 #logging.logging_conf_file = /path/to/custom_logging.ini
553
554 ; Dummy marker to add new entries after.
555 ; Add any custom entries below. Please don't remove this marker.
577 556 custom.conf = 1
578 557
579 558
580 ################################
581 ### LOGGING CONFIGURATION ####
582 ################################
559 ; #####################
560 ; LOGGING CONFIGURATION
561 ; #####################
583 562 [loggers]
584 563 keys = root, sqlalchemy, beaker, rhodecode, ssh_wrapper
585 564
586 565 [handlers]
587 566 keys = console, console_sql
588 567
589 568 [formatters]
590 569 keys = generic, color_formatter, color_formatter_sql
591 570
592 #############
593 ## LOGGERS ##
594 #############
571 ; #######
572 ; LOGGERS
573 ; #######
595 574 [logger_root]
596 575 level = NOTSET
597 576 handlers = console
598 577
599 578 [logger_routes]
600 579 level = DEBUG
601 580 handlers =
602 581 qualname = routes.middleware
603 582 ## "level = DEBUG" logs the route matched and routing variables.
604 583 propagate = 1
605 584
585 [logger_sqlalchemy]
586 level = INFO
587 handlers = console_sql
588 qualname = sqlalchemy.engine
589 propagate = 0
590
606 591 [logger_beaker]
607 592 level = DEBUG
608 593 handlers =
609 594 qualname = beaker.container
610 595 propagate = 1
611 596
612 597 [logger_rhodecode]
613 598 level = DEBUG
614 599 handlers =
615 600 qualname = rhodecode
616 601 propagate = 1
617 602
618 [logger_sqlalchemy]
619 level = ERROR
620 handlers = console_sql
621 qualname = sqlalchemy.engine
622 propagate = 0
623
624 603 [logger_ssh_wrapper]
625 604 level = DEBUG
626 605 handlers =
627 606 qualname = ssh_wrapper
628 607 propagate = 1
629 608
609 [logger_celery]
610 level = DEBUG
611 handlers =
612 qualname = celery
630 613
631 ##############
632 ## HANDLERS ##
633 ##############
614
615 ; ########
616 ; HANDLERS
617 ; ########
634 618
635 619 [handler_console]
636 620 class = StreamHandler
637 621 args = (sys.stderr,)
638 622 level = DEBUG
639 623 formatter = generic
624 ; To enable JSON formatted logs replace generic with json
625 ; This allows sending properly formatted logs to grafana loki or elasticsearch
626 #formatter = json
640 627
641 628 [handler_console_sql]
629 ; "level = DEBUG" logs SQL queries and results.
630 ; "level = INFO" logs SQL queries.
631 ; "level = WARN" logs neither. (Recommended for production systems.)
642 632 class = StreamHandler
643 633 args = (sys.stderr,)
644 634 level = WARN
645 635 formatter = generic
646 636
647 ################
648 ## FORMATTERS ##
649 ################
637 ; ##########
638 ; FORMATTERS
639 ; ##########
650 640
651 641 [formatter_generic]
652 642 class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter
653 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
643 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
654 644 datefmt = %Y-%m-%d %H:%M:%S
655 645
656 646 [formatter_color_formatter]
657 647 class = rhodecode.lib.logging_formatter.ColorFormatter
658 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
648 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
659 649 datefmt = %Y-%m-%d %H:%M:%S
660 650
661 651 [formatter_color_formatter_sql]
662 652 class = rhodecode.lib.logging_formatter.ColorFormatterSql
663 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
653 format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s
664 654 datefmt = %Y-%m-%d %H:%M:%S
655
656 [formatter_json]
657 format = %(message)s
658 class = rhodecode.lib._vendor.jsonlogger.JsonFormatter No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now