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