##// END OF EJS Templates
http: Remove default arguments from prepare callback function.
Martin Bornhold -
r961:85c844b7 default
parent child Browse files
Show More
@@ -1,279 +1,278 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import json
22 22 import logging
23 23 import urlparse
24 24 import threading
25 25 from BaseHTTPServer import BaseHTTPRequestHandler
26 26 from SocketServer import TCPServer
27 27 from routes.util import URLGenerator
28 28
29 29 import Pyro4
30 30 import pylons
31 31 import rhodecode
32 32
33 33 from rhodecode.model import meta
34 34 from rhodecode.lib import hooks_base
35 35 from rhodecode.lib.utils2 import (
36 36 AttributeDict, safe_str, get_routes_generator_for_server_url)
37 37
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41
42 42 class HooksHttpHandler(BaseHTTPRequestHandler):
43 43 def do_POST(self):
44 44 method, extras = self._read_request()
45 45 try:
46 46 result = self._call_hook(method, extras)
47 47 except Exception as e:
48 48 result = {
49 49 'exception': e.__class__.__name__,
50 50 'exception_args': e.args
51 51 }
52 52 self._write_response(result)
53 53
54 54 def _read_request(self):
55 55 length = int(self.headers['Content-Length'])
56 56 body = self.rfile.read(length).decode('utf-8')
57 57 data = json.loads(body)
58 58 return data['method'], data['extras']
59 59
60 60 def _write_response(self, result):
61 61 self.send_response(200)
62 62 self.send_header("Content-type", "text/json")
63 63 self.end_headers()
64 64 self.wfile.write(json.dumps(result))
65 65
66 66 def _call_hook(self, method, extras):
67 67 hooks = Hooks()
68 68 try:
69 69 result = getattr(hooks, method)(extras)
70 70 finally:
71 71 meta.Session.remove()
72 72 return result
73 73
74 74 def log_message(self, format, *args):
75 75 """
76 76 This is an overriden method of BaseHTTPRequestHandler which logs using
77 77 logging library instead of writing directly to stderr.
78 78 """
79 79
80 80 message = format % args
81 81
82 82 # TODO: mikhail: add different log levels support
83 83 log.debug(
84 84 "%s - - [%s] %s", self.client_address[0],
85 85 self.log_date_time_string(), message)
86 86
87 87
88 88 class DummyHooksCallbackDaemon(object):
89 89 def __init__(self):
90 90 self.hooks_module = Hooks.__module__
91 91
92 92 def __enter__(self):
93 93 log.debug('Running dummy hooks callback daemon')
94 94 return self
95 95
96 96 def __exit__(self, exc_type, exc_val, exc_tb):
97 97 log.debug('Exiting dummy hooks callback daemon')
98 98
99 99
100 100 class ThreadedHookCallbackDaemon(object):
101 101
102 102 _callback_thread = None
103 103 _daemon = None
104 104 _done = False
105 105
106 106 def __init__(self):
107 107 self._prepare()
108 108
109 109 def __enter__(self):
110 110 self._run()
111 111 return self
112 112
113 113 def __exit__(self, exc_type, exc_val, exc_tb):
114 114 self._stop()
115 115
116 116 def _prepare(self):
117 117 raise NotImplementedError()
118 118
119 119 def _run(self):
120 120 raise NotImplementedError()
121 121
122 122 def _stop(self):
123 123 raise NotImplementedError()
124 124
125 125
126 126 class Pyro4HooksCallbackDaemon(ThreadedHookCallbackDaemon):
127 127 """
128 128 Context manager which will run a callback daemon in a background thread.
129 129 """
130 130
131 131 hooks_uri = None
132 132
133 133 def _prepare(self):
134 134 log.debug("Preparing callback daemon and registering hook object")
135 135 self._daemon = Pyro4.Daemon()
136 136 hooks_interface = Hooks()
137 137 self.hooks_uri = str(self._daemon.register(hooks_interface))
138 138 log.debug("Hooks uri is: %s", self.hooks_uri)
139 139
140 140 def _run(self):
141 141 log.debug("Running event loop of callback daemon in background thread")
142 142 callback_thread = threading.Thread(
143 143 target=self._daemon.requestLoop,
144 144 kwargs={'loopCondition': lambda: not self._done})
145 145 callback_thread.daemon = True
146 146 callback_thread.start()
147 147 self._callback_thread = callback_thread
148 148
149 149 def _stop(self):
150 150 log.debug("Waiting for background thread to finish.")
151 151 self._done = True
152 152 self._callback_thread.join()
153 153 self._daemon.close()
154 154 self._daemon = None
155 155 self._callback_thread = None
156 156
157 157
158 158 class HttpHooksCallbackDaemon(ThreadedHookCallbackDaemon):
159 159 """
160 160 Context manager which will run a callback daemon in a background thread.
161 161 """
162 162
163 163 hooks_uri = None
164 164
165 165 IP_ADDRESS = '127.0.0.1'
166 166
167 167 # From Python docs: Polling reduces our responsiveness to a shutdown
168 168 # request and wastes cpu at all other times.
169 169 POLL_INTERVAL = 0.1
170 170
171 171 def _prepare(self):
172 172 log.debug("Preparing callback daemon and registering hook object")
173 173
174 174 self._done = False
175 175 self._daemon = TCPServer((self.IP_ADDRESS, 0), HooksHttpHandler)
176 176 _, port = self._daemon.server_address
177 177 self.hooks_uri = '{}:{}'.format(self.IP_ADDRESS, port)
178 178
179 179 log.debug("Hooks uri is: %s", self.hooks_uri)
180 180
181 181 def _run(self):
182 182 log.debug("Running event loop of callback daemon in background thread")
183 183 callback_thread = threading.Thread(
184 184 target=self._daemon.serve_forever,
185 185 kwargs={'poll_interval': self.POLL_INTERVAL})
186 186 callback_thread.daemon = True
187 187 callback_thread.start()
188 188 self._callback_thread = callback_thread
189 189
190 190 def _stop(self):
191 191 log.debug("Waiting for background thread to finish.")
192 192 self._daemon.shutdown()
193 193 self._callback_thread.join()
194 194 self._daemon = None
195 195 self._callback_thread = None
196 196
197 197
198 def prepare_callback_daemon(extras, protocol=None, use_direct_calls=False):
198 def prepare_callback_daemon(extras, protocol, use_direct_calls):
199 199 callback_daemon = None
200 protocol = protocol.lower() if protocol else None
201 200
202 201 if use_direct_calls:
203 202 callback_daemon = DummyHooksCallbackDaemon()
204 203 extras['hooks_module'] = callback_daemon.hooks_module
205 204 else:
206 205 if protocol == 'pyro4':
207 206 callback_daemon = Pyro4HooksCallbackDaemon()
208 207 elif protocol == 'http':
209 208 callback_daemon = HttpHooksCallbackDaemon()
210 209 else:
211 210 log.error('Unsupported callback daemon protocol "%s"', protocol)
212 211 raise Exception('Unsupported callback daemon protocol.')
213 212
214 213 extras['hooks_uri'] = callback_daemon.hooks_uri
215 214 extras['hooks_protocol'] = protocol
216 215
217 216 return callback_daemon, extras
218 217
219 218
220 219 class Hooks(object):
221 220 """
222 221 Exposes the hooks for remote call backs
223 222 """
224 223
225 224 @Pyro4.callback
226 225 def repo_size(self, extras):
227 226 log.debug("Called repo_size of Hooks object")
228 227 return self._call_hook(hooks_base.repo_size, extras)
229 228
230 229 @Pyro4.callback
231 230 def pre_pull(self, extras):
232 231 log.debug("Called pre_pull of Hooks object")
233 232 return self._call_hook(hooks_base.pre_pull, extras)
234 233
235 234 @Pyro4.callback
236 235 def post_pull(self, extras):
237 236 log.debug("Called post_pull of Hooks object")
238 237 return self._call_hook(hooks_base.post_pull, extras)
239 238
240 239 @Pyro4.callback
241 240 def pre_push(self, extras):
242 241 log.debug("Called pre_push of Hooks object")
243 242 return self._call_hook(hooks_base.pre_push, extras)
244 243
245 244 @Pyro4.callback
246 245 def post_push(self, extras):
247 246 log.debug("Called post_push of Hooks object")
248 247 return self._call_hook(hooks_base.post_push, extras)
249 248
250 249 def _call_hook(self, hook, extras):
251 250 extras = AttributeDict(extras)
252 251 pylons_router = get_routes_generator_for_server_url(extras.server_url)
253 252 pylons.url._push_object(pylons_router)
254 253
255 254 try:
256 255 result = hook(extras)
257 256 except Exception as error:
258 257 log.exception('Exception when handling hook %s', hook)
259 258 error_args = error.args
260 259 return {
261 260 'status': 128,
262 261 'output': '',
263 262 'exception': type(error).__name__,
264 263 'exception_args': error_args,
265 264 }
266 265 finally:
267 266 pylons.url._pop_object()
268 267 meta.Session.remove()
269 268
270 269 return {
271 270 'status': result.status,
272 271 'output': result.output,
273 272 }
274 273
275 274 def __enter__(self):
276 275 return self
277 276
278 277 def __exit__(self, exc_type, exc_val, exc_tb):
279 278 pass
General Comments 0
You need to be logged in to leave comments. Login now