##// END OF EJS Templates
hooks: added extra logging.
marcink -
r1455:7e5ee316 default
parent child Browse files
Show More
@@ -1,389 +1,395 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2013-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
22 22 """
23 23 Set of hooks run by RhodeCode Enterprise
24 24 """
25 25
26 26 import os
27 27 import collections
28 import logging
28 29
29 30 import rhodecode
30 31 from rhodecode import events
31 32 from rhodecode.lib import helpers as h
32 33 from rhodecode.lib.utils import action_logger
33 34 from rhodecode.lib.utils2 import safe_str
34 35 from rhodecode.lib.exceptions import HTTPLockedRC, UserCreationError
35 36 from rhodecode.model.db import Repository, User
36 37
38 log = logging.getLogger(__name__)
39
37 40
38 41 HookResponse = collections.namedtuple('HookResponse', ('status', 'output'))
39 42
40 43
41 44 def is_shadow_repo(extras):
42 45 """
43 46 Returns ``True`` if this is an action executed against a shadow repository.
44 47 """
45 48 return extras['is_shadow_repo']
46 49
47 50
48 51 def _get_scm_size(alias, root_path):
49 52
50 53 if not alias.startswith('.'):
51 54 alias += '.'
52 55
53 56 size_scm, size_root = 0, 0
54 57 for path, unused_dirs, files in os.walk(safe_str(root_path)):
55 58 if path.find(alias) != -1:
56 59 for f in files:
57 60 try:
58 61 size_scm += os.path.getsize(os.path.join(path, f))
59 62 except OSError:
60 63 pass
61 64 else:
62 65 for f in files:
63 66 try:
64 67 size_root += os.path.getsize(os.path.join(path, f))
65 68 except OSError:
66 69 pass
67 70
68 71 size_scm_f = h.format_byte_size_binary(size_scm)
69 72 size_root_f = h.format_byte_size_binary(size_root)
70 73 size_total_f = h.format_byte_size_binary(size_root + size_scm)
71 74
72 75 return size_scm_f, size_root_f, size_total_f
73 76
74 77
75 78 # actual hooks called by Mercurial internally, and GIT by our Python Hooks
76 79 def repo_size(extras):
77 80 """Present size of repository after push."""
78 81 repo = Repository.get_by_repo_name(extras.repository)
79 82 vcs_part = safe_str(u'.%s' % repo.repo_type)
80 83 size_vcs, size_root, size_total = _get_scm_size(vcs_part,
81 84 repo.repo_full_path)
82 85 msg = ('Repository `%s` size summary %s:%s repo:%s total:%s\n'
83 86 % (repo.repo_name, vcs_part, size_vcs, size_root, size_total))
84 87 return HookResponse(0, msg)
85 88
86 89
87 90 def pre_push(extras):
88 91 """
89 92 Hook executed before pushing code.
90 93
91 94 It bans pushing when the repository is locked.
92 95 """
93 96 usr = User.get_by_username(extras.username)
94
95 97 output = ''
96 98 if extras.locked_by[0] and usr.user_id != int(extras.locked_by[0]):
97 99 locked_by = User.get(extras.locked_by[0]).username
98 100 reason = extras.locked_by[2]
99 101 # this exception is interpreted in git/hg middlewares and based
100 102 # on that proper return code is server to client
101 103 _http_ret = HTTPLockedRC(
102 104 _locked_by_explanation(extras.repository, locked_by, reason))
103 105 if str(_http_ret.code).startswith('2'):
104 106 # 2xx Codes don't raise exceptions
105 107 output = _http_ret.title
106 108 else:
107 109 raise _http_ret
108 110
109 111 # Propagate to external components. This is done after checking the
110 112 # lock, for consistent behavior.
111 113 if not is_shadow_repo(extras):
112 114 pre_push_extension(repo_store_path=Repository.base_path(), **extras)
113 115 events.trigger(events.RepoPrePushEvent(
114 116 repo_name=extras.repository, extras=extras))
115 117
116 118 return HookResponse(0, output)
117 119
118 120
119 121 def pre_pull(extras):
120 122 """
121 123 Hook executed before pulling the code.
122 124
123 125 It bans pulling when the repository is locked.
124 126 """
125 127
126 128 output = ''
127 129 if extras.locked_by[0]:
128 130 locked_by = User.get(extras.locked_by[0]).username
129 131 reason = extras.locked_by[2]
130 132 # this exception is interpreted in git/hg middlewares and based
131 133 # on that proper return code is server to client
132 134 _http_ret = HTTPLockedRC(
133 135 _locked_by_explanation(extras.repository, locked_by, reason))
134 136 if str(_http_ret.code).startswith('2'):
135 137 # 2xx Codes don't raise exceptions
136 138 output = _http_ret.title
137 139 else:
138 140 raise _http_ret
139 141
140 142 # Propagate to external components. This is done after checking the
141 143 # lock, for consistent behavior.
142 144 if not is_shadow_repo(extras):
143 145 pre_pull_extension(**extras)
144 146 events.trigger(events.RepoPrePullEvent(
145 147 repo_name=extras.repository, extras=extras))
146 148
147 149 return HookResponse(0, output)
148 150
149 151
150 152 def post_pull(extras):
151 153 """Hook executed after client pulls the code."""
152 154 user = User.get_by_username(extras.username)
153 155 action = 'pull'
154 156 action_logger(user, action, extras.repository, extras.ip, commit=True)
155 157
156 158 # Propagate to external components.
157 159 if not is_shadow_repo(extras):
158 160 post_pull_extension(**extras)
159 161 events.trigger(events.RepoPullEvent(
160 162 repo_name=extras.repository, extras=extras))
161 163
162 164 output = ''
163 165 # make lock is a tri state False, True, None. We only make lock on True
164 166 if extras.make_lock is True and not is_shadow_repo(extras):
165 167 Repository.lock(Repository.get_by_repo_name(extras.repository),
166 168 user.user_id,
167 169 lock_reason=Repository.LOCK_PULL)
168 170 msg = 'Made lock on repo `%s`' % (extras.repository,)
169 171 output += msg
170 172
171 173 if extras.locked_by[0]:
172 174 locked_by = User.get(extras.locked_by[0]).username
173 175 reason = extras.locked_by[2]
174 176 _http_ret = HTTPLockedRC(
175 177 _locked_by_explanation(extras.repository, locked_by, reason))
176 178 if str(_http_ret.code).startswith('2'):
177 179 # 2xx Codes don't raise exceptions
178 180 output += _http_ret.title
179 181
180 182 return HookResponse(0, output)
181 183
182 184
183 185 def post_push(extras):
184 186 """Hook executed after user pushes to the repository."""
185 187 action_tmpl = extras.action + ':%s'
186 188 commit_ids = extras.commit_ids[:29000]
187 189
188 190 action = action_tmpl % ','.join(commit_ids)
189 191 action_logger(
190 192 extras.username, action, extras.repository, extras.ip, commit=True)
191 193
192 194 # Propagate to external components.
193 195 if not is_shadow_repo(extras):
194 196 post_push_extension(
195 197 repo_store_path=Repository.base_path(),
196 198 pushed_revs=commit_ids,
197 199 **extras)
198 200 events.trigger(events.RepoPushEvent(
199 201 repo_name=extras.repository,
200 202 pushed_commit_ids=commit_ids,
201 203 extras=extras))
202 204
203 205 output = ''
204 206 # make lock is a tri state False, True, None. We only release lock on False
205 207 if extras.make_lock is False and not is_shadow_repo(extras):
206 208 Repository.unlock(Repository.get_by_repo_name(extras.repository))
207 209 msg = 'Released lock on repo `%s`\n' % extras.repository
208 210 output += msg
209 211
210 212 if extras.locked_by[0]:
211 213 locked_by = User.get(extras.locked_by[0]).username
212 214 reason = extras.locked_by[2]
213 215 _http_ret = HTTPLockedRC(
214 216 _locked_by_explanation(extras.repository, locked_by, reason))
215 217 # TODO: johbo: if not?
216 218 if str(_http_ret.code).startswith('2'):
217 219 # 2xx Codes don't raise exceptions
218 220 output += _http_ret.title
219 221
220 222 output += 'RhodeCode: push completed\n'
221 223
222 224 return HookResponse(0, output)
223 225
224 226
225 227 def _locked_by_explanation(repo_name, user_name, reason):
226 228 message = (
227 229 'Repository `%s` locked by user `%s`. Reason:`%s`'
228 230 % (repo_name, user_name, reason))
229 231 return message
230 232
231 233
232 234 def check_allowed_create_user(user_dict, created_by, **kwargs):
233 235 # pre create hooks
234 236 if pre_create_user.is_active():
235 237 allowed, reason = pre_create_user(created_by=created_by, **user_dict)
236 238 if not allowed:
237 239 raise UserCreationError(reason)
238 240
239 241
240 242 class ExtensionCallback(object):
241 243 """
242 244 Forwards a given call to rcextensions, sanitizes keyword arguments.
243 245
244 246 Does check if there is an extension active for that hook. If it is
245 247 there, it will forward all `kwargs_keys` keyword arguments to the
246 248 extension callback.
247 249 """
248 250
249 251 def __init__(self, hook_name, kwargs_keys):
250 252 self._hook_name = hook_name
251 253 self._kwargs_keys = set(kwargs_keys)
252 254
253 255 def __call__(self, *args, **kwargs):
256 log.debug('Calling extension callback for %s', self._hook_name)
257
254 258 kwargs_to_pass = dict((key, kwargs[key]) for key in self._kwargs_keys)
255 259 callback = self._get_callback()
256 260 if callback:
257 261 return callback(**kwargs_to_pass)
262 else:
263 log.debug('extensions callback not found skipping...')
258 264
259 265 def is_active(self):
260 266 return hasattr(rhodecode.EXTENSIONS, self._hook_name)
261 267
262 268 def _get_callback(self):
263 269 return getattr(rhodecode.EXTENSIONS, self._hook_name, None)
264 270
265 271
266 272 pre_pull_extension = ExtensionCallback(
267 273 hook_name='PRE_PULL_HOOK',
268 274 kwargs_keys=(
269 275 'server_url', 'config', 'scm', 'username', 'ip', 'action',
270 276 'repository'))
271 277
272 278
273 279 post_pull_extension = ExtensionCallback(
274 280 hook_name='PULL_HOOK',
275 281 kwargs_keys=(
276 282 'server_url', 'config', 'scm', 'username', 'ip', 'action',
277 283 'repository'))
278 284
279 285
280 286 pre_push_extension = ExtensionCallback(
281 287 hook_name='PRE_PUSH_HOOK',
282 288 kwargs_keys=(
283 289 'server_url', 'config', 'scm', 'username', 'ip', 'action',
284 290 'repository', 'repo_store_path'))
285 291
286 292
287 293 post_push_extension = ExtensionCallback(
288 294 hook_name='PUSH_HOOK',
289 295 kwargs_keys=(
290 296 'server_url', 'config', 'scm', 'username', 'ip', 'action',
291 297 'repository', 'repo_store_path', 'pushed_revs'))
292 298
293 299
294 300 pre_create_user = ExtensionCallback(
295 301 hook_name='PRE_CREATE_USER_HOOK',
296 302 kwargs_keys=(
297 303 'username', 'password', 'email', 'firstname', 'lastname', 'active',
298 304 'admin', 'created_by'))
299 305
300 306
301 307 log_create_pull_request = ExtensionCallback(
302 308 hook_name='CREATE_PULL_REQUEST',
303 309 kwargs_keys=(
304 310 'server_url', 'config', 'scm', 'username', 'ip', 'action',
305 311 'repository', 'pull_request_id', 'url', 'title', 'description',
306 312 'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
307 313 'mergeable', 'source', 'target', 'author', 'reviewers'))
308 314
309 315
310 316 log_merge_pull_request = ExtensionCallback(
311 317 hook_name='MERGE_PULL_REQUEST',
312 318 kwargs_keys=(
313 319 'server_url', 'config', 'scm', 'username', 'ip', 'action',
314 320 'repository', 'pull_request_id', 'url', 'title', 'description',
315 321 'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
316 322 'mergeable', 'source', 'target', 'author', 'reviewers'))
317 323
318 324
319 325 log_close_pull_request = ExtensionCallback(
320 326 hook_name='CLOSE_PULL_REQUEST',
321 327 kwargs_keys=(
322 328 'server_url', 'config', 'scm', 'username', 'ip', 'action',
323 329 'repository', 'pull_request_id', 'url', 'title', 'description',
324 330 'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
325 331 'mergeable', 'source', 'target', 'author', 'reviewers'))
326 332
327 333
328 334 log_review_pull_request = ExtensionCallback(
329 335 hook_name='REVIEW_PULL_REQUEST',
330 336 kwargs_keys=(
331 337 'server_url', 'config', 'scm', 'username', 'ip', 'action',
332 338 'repository', 'pull_request_id', 'url', 'title', 'description',
333 339 'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
334 340 'mergeable', 'source', 'target', 'author', 'reviewers'))
335 341
336 342
337 343 log_update_pull_request = ExtensionCallback(
338 344 hook_name='UPDATE_PULL_REQUEST',
339 345 kwargs_keys=(
340 346 'server_url', 'config', 'scm', 'username', 'ip', 'action',
341 347 'repository', 'pull_request_id', 'url', 'title', 'description',
342 348 'status', 'created_on', 'updated_on', 'commit_ids', 'review_status',
343 349 'mergeable', 'source', 'target', 'author', 'reviewers'))
344 350
345 351
346 352 log_create_user = ExtensionCallback(
347 353 hook_name='CREATE_USER_HOOK',
348 354 kwargs_keys=(
349 355 'username', 'full_name_or_username', 'full_contact', 'user_id',
350 356 'name', 'firstname', 'short_contact', 'admin', 'lastname',
351 357 'ip_addresses', 'extern_type', 'extern_name',
352 358 'email', 'api_key', 'api_keys', 'last_login',
353 359 'full_name', 'active', 'password', 'emails',
354 360 'inherit_default_permissions', 'created_by', 'created_on'))
355 361
356 362
357 363 log_delete_user = ExtensionCallback(
358 364 hook_name='DELETE_USER_HOOK',
359 365 kwargs_keys=(
360 366 'username', 'full_name_or_username', 'full_contact', 'user_id',
361 367 'name', 'firstname', 'short_contact', 'admin', 'lastname',
362 368 'ip_addresses',
363 369 'email', 'api_key', 'last_login',
364 370 'full_name', 'active', 'password', 'emails',
365 371 'inherit_default_permissions', 'deleted_by'))
366 372
367 373
368 374 log_create_repository = ExtensionCallback(
369 375 hook_name='CREATE_REPO_HOOK',
370 376 kwargs_keys=(
371 377 'repo_name', 'repo_type', 'description', 'private', 'created_on',
372 378 'enable_downloads', 'repo_id', 'user_id', 'enable_statistics',
373 379 'clone_uri', 'fork_id', 'group_id', 'created_by'))
374 380
375 381
376 382 log_delete_repository = ExtensionCallback(
377 383 hook_name='DELETE_REPO_HOOK',
378 384 kwargs_keys=(
379 385 'repo_name', 'repo_type', 'description', 'private', 'created_on',
380 386 'enable_downloads', 'repo_id', 'user_id', 'enable_statistics',
381 387 'clone_uri', 'fork_id', 'group_id', 'deleted_by', 'deleted_on'))
382 388
383 389
384 390 log_create_repository_group = ExtensionCallback(
385 391 hook_name='CREATE_REPO_GROUP_HOOK',
386 392 kwargs_keys=(
387 393 'group_name', 'group_parent_id', 'group_description',
388 394 'group_id', 'user_id', 'created_by', 'created_on',
389 395 'enable_locking'))
General Comments 0
You need to be logged in to leave comments. Login now