##// END OF EJS Templates
http: Remove 'pyro4' default values from function arguemnts in vcs init. #4237
Martin Bornhold -
r959:b3b912d4 default
parent child Browse files
Show More
@@ -1,290 +1,290 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2016 RhodeCode GmbH
3 # Copyright (C) 2014-2016 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 Various version Control System version lib (vcs) management abstraction layer
22 Various version Control System version lib (vcs) management abstraction layer
23 for Python. Build with server client architecture.
23 for Python. Build with server client architecture.
24 """
24 """
25
25
26
26
27 VERSION = (0, 5, 0, 'dev')
27 VERSION = (0, 5, 0, 'dev')
28
28
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
30
30
31 __all__ = [
31 __all__ = [
32 'get_version', 'get_vcs_instance', 'get_backend',
32 'get_version', 'get_vcs_instance', 'get_backend',
33 'VCSError', 'RepositoryError', 'CommitError'
33 'VCSError', 'RepositoryError', 'CommitError'
34 ]
34 ]
35
35
36 import atexit
36 import atexit
37 import logging
37 import logging
38 import subprocess
38 import subprocess
39 import time
39 import time
40 import urlparse
40 import urlparse
41 from cStringIO import StringIO
41 from cStringIO import StringIO
42
42
43 import Pyro4
43 import Pyro4
44 from Pyro4.errors import CommunicationError
44 from Pyro4.errors import CommunicationError
45
45
46 from rhodecode.lib.vcs.conf import settings
46 from rhodecode.lib.vcs.conf import settings
47 from rhodecode.lib.vcs.backends import get_vcs_instance, get_backend
47 from rhodecode.lib.vcs.backends import get_vcs_instance, get_backend
48 from rhodecode.lib.vcs.exceptions import (
48 from rhodecode.lib.vcs.exceptions import (
49 VCSError, RepositoryError, CommitError)
49 VCSError, RepositoryError, CommitError)
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53 # The pycurl library directly accesses C API functions and is not patched by
53 # The pycurl library directly accesses C API functions and is not patched by
54 # gevent. This will potentially lead to deadlocks due to incompatibility to
54 # gevent. This will potentially lead to deadlocks due to incompatibility to
55 # gevent. Therefore we check if gevent is active and import a gevent compatible
55 # gevent. Therefore we check if gevent is active and import a gevent compatible
56 # wrapper in that case.
56 # wrapper in that case.
57 try:
57 try:
58 from gevent import monkey
58 from gevent import monkey
59 if monkey.is_module_patched('__builtin__'):
59 if monkey.is_module_patched('__builtin__'):
60 import geventcurl as pycurl
60 import geventcurl as pycurl
61 log.debug('Using gevent comapatible pycurl: %s', pycurl)
61 log.debug('Using gevent comapatible pycurl: %s', pycurl)
62 else:
62 else:
63 import pycurl
63 import pycurl
64 except ImportError:
64 except ImportError:
65 import pycurl
65 import pycurl
66
66
67
67
68 def get_version():
68 def get_version():
69 """
69 """
70 Returns shorter version (digit parts only) as string.
70 Returns shorter version (digit parts only) as string.
71 """
71 """
72 return '.'.join((str(each) for each in VERSION[:3]))
72 return '.'.join((str(each) for each in VERSION[:3]))
73
73
74
74
75 def connect_pyro4(server_and_port):
75 def connect_pyro4(server_and_port):
76 from rhodecode.lib.vcs import connection, client
76 from rhodecode.lib.vcs import connection, client
77 from rhodecode.lib.middleware.utils import scm_app
77 from rhodecode.lib.middleware.utils import scm_app
78
78
79 git_remote = client.RequestScopeProxyFactory(
79 git_remote = client.RequestScopeProxyFactory(
80 settings.pyro_remote(settings.PYRO_GIT, server_and_port))
80 settings.pyro_remote(settings.PYRO_GIT, server_and_port))
81 hg_remote = client.RequestScopeProxyFactory(
81 hg_remote = client.RequestScopeProxyFactory(
82 settings.pyro_remote(settings.PYRO_HG, server_and_port))
82 settings.pyro_remote(settings.PYRO_HG, server_and_port))
83 svn_remote = client.RequestScopeProxyFactory(
83 svn_remote = client.RequestScopeProxyFactory(
84 settings.pyro_remote(settings.PYRO_SVN, server_and_port))
84 settings.pyro_remote(settings.PYRO_SVN, server_and_port))
85
85
86 connection.Git = client.RepoMaker(proxy_factory=git_remote)
86 connection.Git = client.RepoMaker(proxy_factory=git_remote)
87 connection.Hg = client.RepoMaker(proxy_factory=hg_remote)
87 connection.Hg = client.RepoMaker(proxy_factory=hg_remote)
88 connection.Svn = client.RepoMaker(proxy_factory=svn_remote)
88 connection.Svn = client.RepoMaker(proxy_factory=svn_remote)
89
89
90 scm_app.GIT_REMOTE_WSGI = Pyro4.Proxy(
90 scm_app.GIT_REMOTE_WSGI = Pyro4.Proxy(
91 settings.pyro_remote(
91 settings.pyro_remote(
92 settings.PYRO_GIT_REMOTE_WSGI, server_and_port))
92 settings.PYRO_GIT_REMOTE_WSGI, server_and_port))
93 scm_app.HG_REMOTE_WSGI = Pyro4.Proxy(
93 scm_app.HG_REMOTE_WSGI = Pyro4.Proxy(
94 settings.pyro_remote(
94 settings.pyro_remote(
95 settings.PYRO_HG_REMOTE_WSGI, server_and_port))
95 settings.PYRO_HG_REMOTE_WSGI, server_and_port))
96
96
97 @atexit.register
97 @atexit.register
98 def free_connection_resources():
98 def free_connection_resources():
99 connection.Git = None
99 connection.Git = None
100 connection.Hg = None
100 connection.Hg = None
101 connection.Svn = None
101 connection.Svn = None
102
102
103
103
104 def connect_http(server_and_port):
104 def connect_http(server_and_port):
105 from rhodecode.lib.vcs import connection, client_http
105 from rhodecode.lib.vcs import connection, client_http
106 from rhodecode.lib.middleware.utils import scm_app
106 from rhodecode.lib.middleware.utils import scm_app
107
107
108 session_factory = client_http.ThreadlocalSessionFactory()
108 session_factory = client_http.ThreadlocalSessionFactory()
109
109
110 connection.Git = client_http.RepoMaker(
110 connection.Git = client_http.RepoMaker(
111 server_and_port, '/git', session_factory)
111 server_and_port, '/git', session_factory)
112 connection.Hg = client_http.RepoMaker(
112 connection.Hg = client_http.RepoMaker(
113 server_and_port, '/hg', session_factory)
113 server_and_port, '/hg', session_factory)
114 connection.Svn = client_http.RepoMaker(
114 connection.Svn = client_http.RepoMaker(
115 server_and_port, '/svn', session_factory)
115 server_and_port, '/svn', session_factory)
116
116
117 scm_app.HG_REMOTE_WSGI = client_http.VcsHttpProxy(
117 scm_app.HG_REMOTE_WSGI = client_http.VcsHttpProxy(
118 server_and_port, '/proxy/hg')
118 server_and_port, '/proxy/hg')
119 scm_app.GIT_REMOTE_WSGI = client_http.VcsHttpProxy(
119 scm_app.GIT_REMOTE_WSGI = client_http.VcsHttpProxy(
120 server_and_port, '/proxy/git')
120 server_and_port, '/proxy/git')
121
121
122 @atexit.register
122 @atexit.register
123 def free_connection_resources():
123 def free_connection_resources():
124 connection.Git = None
124 connection.Git = None
125 connection.Hg = None
125 connection.Hg = None
126 connection.Svn = None
126 connection.Svn = None
127
127
128
128
129 def connect_vcs(server_and_port, protocol='pyro4'):
129 def connect_vcs(server_and_port, protocol):
130 """
130 """
131 Initializes the connection to the vcs server.
131 Initializes the connection to the vcs server.
132
132
133 :param server_and_port: str, e.g. "localhost:9900"
133 :param server_and_port: str, e.g. "localhost:9900"
134 :param protocol: str, "pyro4" or "http"
134 :param protocol: str, "pyro4" or "http"
135 """
135 """
136 if protocol == 'pyro4':
136 if protocol == 'pyro4':
137 connect_pyro4(server_and_port)
137 connect_pyro4(server_and_port)
138 elif protocol == 'http':
138 elif protocol == 'http':
139 connect_http(server_and_port)
139 connect_http(server_and_port)
140
140
141
141
142 # TODO: johbo: This function should be moved into our test suite, there is
142 # TODO: johbo: This function should be moved into our test suite, there is
143 # no reason to support starting the vcsserver in Enterprise itself.
143 # no reason to support starting the vcsserver in Enterprise itself.
144 def start_vcs_server(server_and_port, protocol='pyro4', log_level=None):
144 def start_vcs_server(server_and_port, protocol, log_level=None):
145 """
145 """
146 Starts the vcs server in a subprocess.
146 Starts the vcs server in a subprocess.
147 """
147 """
148 log.info('Starting VCSServer as a sub process with %s protocol', protocol)
148 log.info('Starting VCSServer as a sub process with %s protocol', protocol)
149 if protocol == 'http':
149 if protocol == 'http':
150 return _start_http_vcs_server(server_and_port, log_level)
150 return _start_http_vcs_server(server_and_port, log_level)
151 elif protocol == 'pyro4':
151 elif protocol == 'pyro4':
152 return _start_pyro4_vcs_server(server_and_port, log_level)
152 return _start_pyro4_vcs_server(server_and_port, log_level)
153
153
154
154
155 def _start_pyro4_vcs_server(server_and_port, log_level=None):
155 def _start_pyro4_vcs_server(server_and_port, log_level=None):
156 _try_to_shutdown_running_server(server_and_port)
156 _try_to_shutdown_running_server(server_and_port, protocol='pyro4')
157 host, port = server_and_port.rsplit(":", 1)
157 host, port = server_and_port.rsplit(":", 1)
158 host = host.strip('[]')
158 host = host.strip('[]')
159 args = [
159 args = [
160 'vcsserver', '--port', port, '--host', host, '--locale', 'en_US.UTF-8',
160 'vcsserver', '--port', port, '--host', host, '--locale', 'en_US.UTF-8',
161 '--threadpool', '32']
161 '--threadpool', '32']
162 if log_level:
162 if log_level:
163 args += ['--log-level', log_level]
163 args += ['--log-level', log_level]
164 proc = subprocess.Popen(args)
164 proc = subprocess.Popen(args)
165
165
166 def cleanup_server_process():
166 def cleanup_server_process():
167 proc.kill()
167 proc.kill()
168 atexit.register(cleanup_server_process)
168 atexit.register(cleanup_server_process)
169
169
170 server = create_vcsserver_proxy(server_and_port, protocol='pyro4')
170 server = create_vcsserver_proxy(server_and_port, protocol='pyro4')
171 _wait_until_vcs_server_is_reachable(server)
171 _wait_until_vcs_server_is_reachable(server)
172
172
173
173
174 def _start_http_vcs_server(server_and_port, log_level=None):
174 def _start_http_vcs_server(server_and_port, log_level=None):
175 # TODO: mikhail: shutdown if an http server already runs
175 # TODO: mikhail: shutdown if an http server already runs
176
176
177 host, port = server_and_port.rsplit(":", 1)
177 host, port = server_and_port.rsplit(":", 1)
178 args = [
178 args = [
179 'pserve', 'vcsserver/development_pyramid.ini',
179 'pserve', 'vcsserver/development_pyramid.ini',
180 'http_port=%s' % (port, ), 'http_host=%s' % (host, )]
180 'http_port=%s' % (port, ), 'http_host=%s' % (host, )]
181 proc = subprocess.Popen(args)
181 proc = subprocess.Popen(args)
182
182
183 def cleanup_server_process():
183 def cleanup_server_process():
184 proc.kill()
184 proc.kill()
185 atexit.register(cleanup_server_process)
185 atexit.register(cleanup_server_process)
186
186
187 server = create_vcsserver_proxy(server_and_port, protocol='http')
187 server = create_vcsserver_proxy(server_and_port, protocol='http')
188 _wait_until_vcs_server_is_reachable(server)
188 _wait_until_vcs_server_is_reachable(server)
189
189
190
190
191 def _wait_until_vcs_server_is_reachable(server):
191 def _wait_until_vcs_server_is_reachable(server):
192 while xrange(80): # max 40s of sleep
192 while xrange(80): # max 40s of sleep
193 try:
193 try:
194 server.ping()
194 server.ping()
195 break
195 break
196 except (CommunicationError, pycurl.error):
196 except (CommunicationError, pycurl.error):
197 pass
197 pass
198 time.sleep(0.5)
198 time.sleep(0.5)
199
199
200
200
201 def _try_to_shutdown_running_server(server_and_port):
201 def _try_to_shutdown_running_server(server_and_port, protocol):
202 server = create_vcsserver_proxy(server_and_port)
202 server = create_vcsserver_proxy(server_and_port, protocol)
203 try:
203 try:
204 server.shutdown()
204 server.shutdown()
205 except (CommunicationError, pycurl.error):
205 except (CommunicationError, pycurl.error):
206 return
206 return
207
207
208 # TODO: Not sure why this is important, but without it the following start
208 # TODO: Not sure why this is important, but without it the following start
209 # of the server fails.
209 # of the server fails.
210 server = create_vcsserver_proxy(server_and_port)
210 server = create_vcsserver_proxy(server_and_port, protocol)
211 server.ping()
211 server.ping()
212
212
213
213
214 def create_vcsserver_proxy(server_and_port, protocol='pyro4'):
214 def create_vcsserver_proxy(server_and_port, protocol):
215 if protocol == 'pyro4':
215 if protocol == 'pyro4':
216 return _create_vcsserver_proxy_pyro4(server_and_port)
216 return _create_vcsserver_proxy_pyro4(server_and_port)
217 elif protocol == 'http':
217 elif protocol == 'http':
218 return _create_vcsserver_proxy_http(server_and_port)
218 return _create_vcsserver_proxy_http(server_and_port)
219
219
220
220
221 def _create_vcsserver_proxy_pyro4(server_and_port):
221 def _create_vcsserver_proxy_pyro4(server_and_port):
222 server = Pyro4.Proxy(
222 server = Pyro4.Proxy(
223 settings.pyro_remote(settings.PYRO_VCSSERVER, server_and_port))
223 settings.pyro_remote(settings.PYRO_VCSSERVER, server_and_port))
224 return server
224 return server
225
225
226
226
227 def _create_vcsserver_proxy_http(server_and_port):
227 def _create_vcsserver_proxy_http(server_and_port):
228 from rhodecode.lib.vcs import client_http
228 from rhodecode.lib.vcs import client_http
229
229
230 session = _create_http_rpc_session()
230 session = _create_http_rpc_session()
231 url = urlparse.urljoin('http://%s' % server_and_port, '/server')
231 url = urlparse.urljoin('http://%s' % server_and_port, '/server')
232 return client_http.RemoteObject(url, session)
232 return client_http.RemoteObject(url, session)
233
233
234
234
235 class CurlSession(object):
235 class CurlSession(object):
236 """
236 """
237 Modeled so that it provides a subset of the requests interface.
237 Modeled so that it provides a subset of the requests interface.
238
238
239 This has been created so that it does only provide a minimal API for our
239 This has been created so that it does only provide a minimal API for our
240 needs. The parts which it provides are based on the API of the library
240 needs. The parts which it provides are based on the API of the library
241 `requests` which allows us to easily benchmark against it.
241 `requests` which allows us to easily benchmark against it.
242
242
243 Please have a look at the class :class:`requests.Session` when you extend
243 Please have a look at the class :class:`requests.Session` when you extend
244 it.
244 it.
245 """
245 """
246
246
247 def __init__(self):
247 def __init__(self):
248 curl = pycurl.Curl()
248 curl = pycurl.Curl()
249 # TODO: johbo: I did test with 7.19 of libcurl. This version has
249 # TODO: johbo: I did test with 7.19 of libcurl. This version has
250 # trouble with 100 - continue being set in the expect header. This
250 # trouble with 100 - continue being set in the expect header. This
251 # can lead to massive performance drops, switching it off here.
251 # can lead to massive performance drops, switching it off here.
252 curl.setopt(curl.HTTPHEADER, ["Expect:"])
252 curl.setopt(curl.HTTPHEADER, ["Expect:"])
253 curl.setopt(curl.TCP_NODELAY, True)
253 curl.setopt(curl.TCP_NODELAY, True)
254 curl.setopt(curl.PROTOCOLS, curl.PROTO_HTTP)
254 curl.setopt(curl.PROTOCOLS, curl.PROTO_HTTP)
255 self._curl = curl
255 self._curl = curl
256
256
257 def post(self, url, data, allow_redirects=False):
257 def post(self, url, data, allow_redirects=False):
258 response_buffer = StringIO()
258 response_buffer = StringIO()
259
259
260 curl = self._curl
260 curl = self._curl
261 curl.setopt(curl.URL, url)
261 curl.setopt(curl.URL, url)
262 curl.setopt(curl.POST, True)
262 curl.setopt(curl.POST, True)
263 curl.setopt(curl.POSTFIELDS, data)
263 curl.setopt(curl.POSTFIELDS, data)
264 curl.setopt(curl.FOLLOWLOCATION, allow_redirects)
264 curl.setopt(curl.FOLLOWLOCATION, allow_redirects)
265 curl.setopt(curl.WRITEDATA, response_buffer)
265 curl.setopt(curl.WRITEDATA, response_buffer)
266 curl.perform()
266 curl.perform()
267
267
268 return CurlResponse(response_buffer)
268 return CurlResponse(response_buffer)
269
269
270
270
271 class CurlResponse(object):
271 class CurlResponse(object):
272 """
272 """
273 The response of a request, modeled after the requests API.
273 The response of a request, modeled after the requests API.
274
274
275 This class provides a subset of the response interface known from the
275 This class provides a subset of the response interface known from the
276 library `requests`. It is intentionally kept similar, so that we can use
276 library `requests`. It is intentionally kept similar, so that we can use
277 `requests` as a drop in replacement for benchmarking purposes.
277 `requests` as a drop in replacement for benchmarking purposes.
278 """
278 """
279
279
280 def __init__(self, response_buffer):
280 def __init__(self, response_buffer):
281 self._response_buffer = response_buffer
281 self._response_buffer = response_buffer
282
282
283 @property
283 @property
284 def content(self):
284 def content(self):
285 return self._response_buffer.getvalue()
285 return self._response_buffer.getvalue()
286
286
287
287
288 def _create_http_rpc_session():
288 def _create_http_rpc_session():
289 session = CurlSession()
289 session = CurlSession()
290 return session
290 return session
General Comments 0
You need to be logged in to leave comments. Login now