Show More
@@ -53,12 +53,6 b' def ArchiveException(org_exc=None):' | |||
|
53 | 53 | return _make_exception_wrapper |
|
54 | 54 | |
|
55 | 55 | |
|
56 | def ClientNotSupportedException(org_exc=None): | |
|
57 | def _make_exception_wrapper(*args): | |
|
58 | return _make_exception('client_not_supported', org_exc, *args) | |
|
59 | return _make_exception_wrapper | |
|
60 | ||
|
61 | ||
|
62 | 56 | def LookupException(org_exc=None): |
|
63 | 57 | def _make_exception_wrapper(*args): |
|
64 | 58 | return _make_exception('lookup', org_exc, *args) |
@@ -82,6 +76,10 b' def RepositoryBranchProtectedException(o' | |||
|
82 | 76 | return _make_exception('repo_branch_protected', org_exc, *args) |
|
83 | 77 | return _make_exception_wrapper |
|
84 | 78 | |
|
79 | def ClientNotSupportedException(org_exc=None): | |
|
80 | def _make_exception_wrapper(*args): | |
|
81 | return _make_exception('client_not_supported', org_exc, *args) | |
|
82 | return _make_exception_wrapper | |
|
85 | 83 | |
|
86 | 84 | def RequirementException(org_exc=None): |
|
87 | 85 | def _make_exception_wrapper(*args): |
@@ -118,9 +116,18 b' class HTTPRepoLocked(HTTPLocked):' | |||
|
118 | 116 | super().__init__(**kwargs) |
|
119 | 117 | |
|
120 | 118 | |
|
121 |
class HTTPRepoBranchProtected(HTTP |
|
|
122 |
def __init__(self, |
|
|
123 | super(HTTPForbidden, self).__init__(*args, **kwargs) | |
|
119 | class HTTPRepoBranchProtected(HTTPLocked): | |
|
120 | def __init__(self, title, status_code=None, **kwargs): | |
|
121 | self.code = status_code or HTTPLocked.code | |
|
122 | self.title = title | |
|
123 | super().__init__(**kwargs) | |
|
124 | ||
|
125 | ||
|
126 | class HTTPClientNotSupported(HTTPLocked): | |
|
127 | def __init__(self, title, status_code=None, **kwargs): | |
|
128 | self.code = status_code or HTTPLocked.code | |
|
129 | self.title = title | |
|
130 | super().__init__(**kwargs) | |
|
124 | 131 | |
|
125 | 132 | |
|
126 | 133 | class RefNotFoundException(KeyError): |
@@ -1,5 +1,5 b'' | |||
|
1 | 1 | # RhodeCode VCSServer provides access to different vcs backends via network. |
|
2 |
# Copyright (C) 2014-202 |
|
|
2 | # Copyright (C) 2014-2024 RhodeCode GmbH | |
|
3 | 3 | # |
|
4 | 4 | # This program is free software; you can redistribute it and/or modify |
|
5 | 5 | # it under the terms of the GNU General Public License as published by |
@@ -56,6 +56,9 b' class HooksCeleryClient:' | |||
|
56 | 56 | self.celery_app = celery_app |
|
57 | 57 | |
|
58 | 58 | def __call__(self, method, extras): |
|
59 | # NOTE: exception handling for those tasks executed is in | |
|
60 | # @adapt_for_celery decorator | |
|
61 | # also see: _maybe_handle_exception which is handling exceptions | |
|
59 | 62 | inquired_task = self.celery_app.signature( |
|
60 | 63 | f'rhodecode.lib.celerylib.tasks.{method}' |
|
61 | 64 | ) |
@@ -83,11 +86,8 b' class HgMessageWriter(RemoteMessageWrite' | |||
|
83 | 86 | self.ui = ui |
|
84 | 87 | |
|
85 | 88 | def write(self, message: str): |
|
86 | # TODO: Check why the quiet flag is set by default. | |
|
87 | old = self.ui.quiet | |
|
88 | self.ui.quiet = False | |
|
89 | self.ui.status(message.encode('utf-8')) | |
|
90 | self.ui.quiet = old | |
|
89 | args = (message.encode('utf-8'),) | |
|
90 | self.ui._writemsg(self.ui._fmsgerr, type=b'status', *args) | |
|
91 | 91 | |
|
92 | 92 | |
|
93 | 93 | class GitMessageWriter(RemoteMessageWriter): |
@@ -97,7 +97,7 b' class GitMessageWriter(RemoteMessageWrit' | |||
|
97 | 97 | self.stdout = stdout or sys.stdout |
|
98 | 98 | |
|
99 | 99 | def write(self, message: str): |
|
100 | self.stdout.write(message) | |
|
100 | self.stdout.write(message + "\n" if message else "") | |
|
101 | 101 | |
|
102 | 102 | |
|
103 | 103 | class SvnMessageWriter(RemoteMessageWriter): |
@@ -111,28 +111,30 b' class SvnMessageWriter(RemoteMessageWrit' | |||
|
111 | 111 | self.stderr.write(message) |
|
112 | 112 | |
|
113 | 113 | |
|
114 | def _maybe_handle_exception(result): | |
|
115 | ||
|
114 | def _maybe_handle_exception(writer, result): | |
|
115 | """ | |
|
116 | adopt_for_celery defines the exception/exception_traceback | |
|
117 | Ths result is a direct output from a celery task | |
|
118 | """ | |
|
116 | 119 | |
|
117 | 120 | exception_class = result.get('exception') |
|
118 | 121 | exception_traceback = result.get('exception_traceback') |
|
119 | if not exception_class: | |
|
120 | return | |
|
121 | 122 | |
|
122 | log.debug('Handling hook-call exception: %s', exception_class) | |
|
123 | ||
|
124 | if exception_traceback: | |
|
125 | log.error('Got traceback from remote call:%s', exception_traceback) | |
|
126 | ||
|
127 | if exception_class == 'HTTPLockedRepo': | |
|
123 | match exception_class: | |
|
124 | # NOTE: the underlying exceptions are setting _vcs_kind special marker | |
|
125 | # which is later handled by `handle_vcs_exception` and translated into a special HTTP exception | |
|
126 | # propagated later to the client | |
|
127 | case 'HTTPLockedRepo': | |
|
128 | 128 | raise exceptions.LockedRepoException()(*result['exception_args']) |
|
129 |
|
|
|
129 | case 'ClientNotSupported': | |
|
130 | 130 | raise exceptions.ClientNotSupportedException()(*result['exception_args']) |
|
131 |
|
|
|
131 | case 'HTTPBranchProtected': | |
|
132 | 132 | raise exceptions.RepositoryBranchProtectedException()(*result['exception_args']) |
|
133 |
|
|
|
133 | case 'RepositoryError': | |
|
134 | 134 | raise exceptions.VcsException()(*result['exception_args']) |
|
135 | elif exception_class: | |
|
135 | case _: | |
|
136 | if exception_class: | |
|
137 | log.error('Handling hook-call exception. Got traceback from remote call:%s', exception_traceback) | |
|
136 | 138 | raise Exception( |
|
137 | 139 | f"""Got remote exception "{exception_class}" with args "{result['exception_args']}" """ |
|
138 | 140 | ) |
@@ -156,7 +158,7 b' def _call_hook(hook_name, extras, writer' | |||
|
156 | 158 | log.debug('Hooks, using client:%s', hooks_client) |
|
157 | 159 | result = hooks_client(hook_name, extras) |
|
158 | 160 | log.debug('Hooks got result: %s', result) |
|
159 | _maybe_handle_exception(result) | |
|
161 | _maybe_handle_exception(writer, result) | |
|
160 | 162 | writer.write(result['output']) |
|
161 | 163 | |
|
162 | 164 | return result['status'] |
@@ -47,7 +47,7 b' from vcsserver.server import VcsServer' | |||
|
47 | 47 | from vcsserver.git_lfs.app import GIT_LFS_CONTENT_TYPE, GIT_LFS_PROTO_PAT |
|
48 | 48 | from vcsserver.echo_stub import remote_wsgi as remote_wsgi_stub |
|
49 | 49 | from vcsserver.echo_stub.echo_app import EchoApp |
|
50 | from vcsserver.exceptions import HTTPRepoLocked, HTTPRepoBranchProtected | |
|
50 | from vcsserver.exceptions import HTTPRepoLocked, HTTPRepoBranchProtected, HTTPClientNotSupported | |
|
51 | 51 | from vcsserver.lib.exc_tracking import store_exception, format_exc |
|
52 | 52 | from vcsserver.lib.str_utils import safe_int |
|
53 | 53 | from vcsserver.lib.statsd_client import StatsdClient |
@@ -635,19 +635,21 b' class HTTPApplication:' | |||
|
635 | 635 | return _git_stream |
|
636 | 636 | |
|
637 | 637 | def handle_vcs_exception(self, exception, request): |
|
638 | _vcs_kind = getattr(exception, '_vcs_kind', '') | |
|
639 | 638 | |
|
640 |
|
|
|
639 | match _vcs_kind := getattr(exception, '_vcs_kind', ''): | |
|
640 | case 'repo_locked': | |
|
641 | 641 | headers_call_context = get_headers_call_context(request.environ) |
|
642 | 642 | status_code = safe_int(headers_call_context['locked_status_code']) |
|
643 | 643 | |
|
644 | 644 | return HTTPRepoLocked( |
|
645 | 645 | title=str(exception), status_code=status_code, headers=[('X-Rc-Locked', '1')]) |
|
646 | ||
|
647 | elif _vcs_kind == 'repo_branch_protected': | |
|
646 | case 'repo_branch_protected': | |
|
648 | 647 | # Get custom repo-branch-protected status code if present. |
|
649 | 648 | return HTTPRepoBranchProtected( |
|
650 | 649 | title=str(exception), headers=[('X-Rc-Branch-Protection', '1')]) |
|
650 | case 'client_not_supported': | |
|
651 | return HTTPClientNotSupported( | |
|
652 | title=str(exception), headers=[('X-Rc-Client-Not-Supported', '1')]) | |
|
651 | 653 | |
|
652 | 654 | exc_info = request.exc_info |
|
653 | 655 | store_exception(id(exc_info), exc_info) |
@@ -415,6 +415,7 b' class SubprocessIOChunker:' | |||
|
415 | 415 | return_code = _p.poll() |
|
416 | 416 | ret_code_ok = return_code in [None, 0] |
|
417 | 417 | ret_code_fail = return_code is not None and return_code != 0 |
|
418 | ||
|
418 | 419 | if ( |
|
419 | 420 | (ret_code_fail and fail_on_return_code) or |
|
420 | 421 | (ret_code_ok and fail_on_stderr and bg_err.length) |
General Comments 0
You need to be logged in to leave comments.
Login now