##// END OF EJS Templates
logging: don't stop logging on "waiting for background thread to finish" This is misleading as we're waiting for something and didn't finalize.
marcink -
r2263:f78c4999 stable
parent child Browse files
Show More
@@ -1,242 +1,243 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 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 traceback
24 24 import threading
25 25 from BaseHTTPServer import BaseHTTPRequestHandler
26 26 from SocketServer import TCPServer
27 27
28 28 import rhodecode
29 29 from rhodecode.model import meta
30 30 from rhodecode.lib.base import bootstrap_request
31 31 from rhodecode.lib import hooks_base
32 32 from rhodecode.lib.utils2 import AttributeDict
33 33
34 34
35 35 log = logging.getLogger(__name__)
36 36
37 37
38 38 class HooksHttpHandler(BaseHTTPRequestHandler):
39 39 def do_POST(self):
40 40 method, extras = self._read_request()
41 41 try:
42 42 result = self._call_hook(method, extras)
43 43 except Exception as e:
44 44 exc_tb = traceback.format_exc()
45 45 result = {
46 46 'exception': e.__class__.__name__,
47 47 'exception_traceback': exc_tb,
48 48 'exception_args': e.args
49 49 }
50 50 self._write_response(result)
51 51
52 52 def _read_request(self):
53 53 length = int(self.headers['Content-Length'])
54 54 body = self.rfile.read(length).decode('utf-8')
55 55 data = json.loads(body)
56 56 return data['method'], data['extras']
57 57
58 58 def _write_response(self, result):
59 59 self.send_response(200)
60 60 self.send_header("Content-type", "text/json")
61 61 self.end_headers()
62 62 self.wfile.write(json.dumps(result))
63 63
64 64 def _call_hook(self, method, extras):
65 65 hooks = Hooks()
66 66 try:
67 67 result = getattr(hooks, method)(extras)
68 68 finally:
69 69 meta.Session.remove()
70 70 return result
71 71
72 72 def log_message(self, format, *args):
73 73 """
74 74 This is an overridden method of BaseHTTPRequestHandler which logs using
75 75 logging library instead of writing directly to stderr.
76 76 """
77 77
78 78 message = format % args
79 79
80 80 # TODO: mikhail: add different log levels support
81 81 log.debug(
82 82 "%s - - [%s] %s", self.client_address[0],
83 83 self.log_date_time_string(), message)
84 84
85 85
86 86 class DummyHooksCallbackDaemon(object):
87 87 def __init__(self):
88 88 self.hooks_module = Hooks.__module__
89 89
90 90 def __enter__(self):
91 91 log.debug('Running dummy hooks callback daemon')
92 92 return self
93 93
94 94 def __exit__(self, exc_type, exc_val, exc_tb):
95 95 log.debug('Exiting dummy hooks callback daemon')
96 96
97 97
98 98 class ThreadedHookCallbackDaemon(object):
99 99
100 100 _callback_thread = None
101 101 _daemon = None
102 102 _done = False
103 103
104 104 def __init__(self):
105 105 self._prepare()
106 106
107 107 def __enter__(self):
108 108 self._run()
109 109 return self
110 110
111 111 def __exit__(self, exc_type, exc_val, exc_tb):
112 112 log.debug('Callback daemon exiting now...')
113 113 self._stop()
114 114
115 115 def _prepare(self):
116 116 raise NotImplementedError()
117 117
118 118 def _run(self):
119 119 raise NotImplementedError()
120 120
121 121 def _stop(self):
122 122 raise NotImplementedError()
123 123
124 124
125 125 class HttpHooksCallbackDaemon(ThreadedHookCallbackDaemon):
126 126 """
127 127 Context manager which will run a callback daemon in a background thread.
128 128 """
129 129
130 130 hooks_uri = None
131 131
132 132 IP_ADDRESS = '127.0.0.1'
133 133
134 134 # From Python docs: Polling reduces our responsiveness to a shutdown
135 135 # request and wastes cpu at all other times.
136 136 POLL_INTERVAL = 0.1
137 137
138 138 def _prepare(self):
139 139 log.debug("Preparing HTTP callback daemon and registering hook object")
140 140
141 141 self._done = False
142 142 self._daemon = TCPServer((self.IP_ADDRESS, 0), HooksHttpHandler)
143 143 _, port = self._daemon.server_address
144 144 self.hooks_uri = '{}:{}'.format(self.IP_ADDRESS, port)
145 145
146 146 log.debug("Hooks uri is: %s", self.hooks_uri)
147 147
148 148 def _run(self):
149 149 log.debug("Running event loop of callback daemon in background thread")
150 150 callback_thread = threading.Thread(
151 151 target=self._daemon.serve_forever,
152 152 kwargs={'poll_interval': self.POLL_INTERVAL})
153 153 callback_thread.daemon = True
154 154 callback_thread.start()
155 155 self._callback_thread = callback_thread
156 156
157 157 def _stop(self):
158 158 log.debug("Waiting for background thread to finish.")
159 159 self._daemon.shutdown()
160 160 self._callback_thread.join()
161 161 self._daemon = None
162 162 self._callback_thread = None
163 log.debug("Background thread done.")
163 164
164 165
165 166 def prepare_callback_daemon(extras, protocol, use_direct_calls):
166 167 callback_daemon = None
167 168
168 169 if use_direct_calls:
169 170 callback_daemon = DummyHooksCallbackDaemon()
170 171 extras['hooks_module'] = callback_daemon.hooks_module
171 172 else:
172 173 if protocol == 'http':
173 174 callback_daemon = HttpHooksCallbackDaemon()
174 175 else:
175 176 log.error('Unsupported callback daemon protocol "%s"', protocol)
176 177 raise Exception('Unsupported callback daemon protocol.')
177 178
178 179 extras['hooks_uri'] = callback_daemon.hooks_uri
179 180 extras['hooks_protocol'] = protocol
180 181
181 182 log.debug('Prepared a callback daemon: %s', callback_daemon)
182 183 return callback_daemon, extras
183 184
184 185
185 186 class Hooks(object):
186 187 """
187 188 Exposes the hooks for remote call backs
188 189 """
189 190
190 191 def repo_size(self, extras):
191 192 log.debug("Called repo_size of %s object", self)
192 193 return self._call_hook(hooks_base.repo_size, extras)
193 194
194 195 def pre_pull(self, extras):
195 196 log.debug("Called pre_pull of %s object", self)
196 197 return self._call_hook(hooks_base.pre_pull, extras)
197 198
198 199 def post_pull(self, extras):
199 200 log.debug("Called post_pull of %s object", self)
200 201 return self._call_hook(hooks_base.post_pull, extras)
201 202
202 203 def pre_push(self, extras):
203 204 log.debug("Called pre_push of %s object", self)
204 205 return self._call_hook(hooks_base.pre_push, extras)
205 206
206 207 def post_push(self, extras):
207 208 log.debug("Called post_push of %s object", self)
208 209 return self._call_hook(hooks_base.post_push, extras)
209 210
210 211 def _call_hook(self, hook, extras):
211 212 extras = AttributeDict(extras)
212 213 server_url = extras['server_url']
213 214
214 215 extras.request = bootstrap_request(application_url=server_url)
215 216
216 217 try:
217 218 result = hook(extras)
218 219 except Exception as error:
219 220 exc_tb = traceback.format_exc()
220 221 log.exception('Exception when handling hook %s', hook)
221 222 error_args = error.args
222 223 return {
223 224 'status': 128,
224 225 'output': '',
225 226 'exception': type(error).__name__,
226 227 'exception_traceback': exc_tb,
227 228 'exception_args': error_args,
228 229 }
229 230 finally:
230 231 meta.Session.remove()
231 232
232 233 log.debug('Got hook call response %s', result)
233 234 return {
234 235 'status': result.status,
235 236 'output': result.output,
236 237 }
237 238
238 239 def __enter__(self):
239 240 return self
240 241
241 242 def __exit__(self, exc_type, exc_val, exc_tb):
242 243 pass
General Comments 0
You need to be logged in to leave comments. Login now