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