Merge pull request #8761 from ThomasWaldmann/fix-remote-modern-exit-codes-master

fix remote repository exception handling / exit codes, fixes #8631
This commit is contained in:
TW 2025-04-21 15:09:07 +02:00 committed by GitHub
commit 4f38dc6031
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 55 additions and 21 deletions

View file

@ -265,6 +265,19 @@ class RepositoryServer: # pragma: no cover
args = self.filter_args(f, args)
res = f(**args)
except BaseException as e:
# These exceptions are reconstructed on the client end in RemoteRepository.call_many(),
# and will be handled just like locally raised exceptions. Suppress the remote traceback
# for these, except ErrorWithTraceback, which should always display a traceback.
reconstructed_exceptions = (
Repository.InvalidRepository,
Repository.InvalidRepositoryConfig,
Repository.DoesNotExist,
Repository.AlreadyExists,
Repository.PathAlreadyExists,
PathNotAllowed,
Repository.InsufficientFreeSpaceError,
Repository.StorageQuotaExceeded,
)
# logger.exception(e)
ex_short = traceback.format_exception_only(e.__class__, e)
ex_full = traceback.format_exception(*sys.exc_info())
@ -272,12 +285,7 @@ class RepositoryServer: # pragma: no cover
if isinstance(e, Error):
ex_short = [e.get_message()]
ex_trace = e.traceback
if isinstance(e, (self.RepoCls.DoesNotExist, self.RepoCls.AlreadyExists, PathNotAllowed)):
# These exceptions are reconstructed on the client end in RemoteRepository*.call_many(),
# and will be handled just like locally raised exceptions. Suppress the remote traceback
# for these, except ErrorWithTraceback, which should always display a traceback.
pass
else:
if not isinstance(e, reconstructed_exceptions):
logging.debug("\n".join(ex_full))
sys_info = sysinfo()
@ -790,6 +798,8 @@ class RemoteRepository:
raise Error(args[0])
elif error == "ErrorWithTraceback":
raise ErrorWithTraceback(args[0])
elif error == "InvalidRepository":
raise Repository.InvalidRepository(self.location.processed)
elif error == "DoesNotExist":
raise Repository.DoesNotExist(self.location.processed)
elif error == "AlreadyExists":
@ -802,6 +812,8 @@ class RemoteRepository:
raise PathNotAllowed(args[0])
elif error == "PathPermissionDenied":
raise Repository.PathPermissionDenied(args[0])
elif error == "PathAlreadyExists":
raise Repository.PathAlreadyExists(args[0])
elif error == "ParentPathDoesNotExist":
raise Repository.ParentPathDoesNotExist(args[0])
elif error == "ObjectNotFound":
@ -818,6 +830,12 @@ class RemoteRepository:
raise NotMyLock(args[0])
elif error == "NoManifestError":
raise NoManifestError
elif error == "InsufficientFreeSpaceError":
raise Repository.InsufficientFreeSpaceError(args[0], args[1])
elif error == "InvalidRepositoryConfig":
raise Repository.InvalidRepositoryConfig(self.location.processed, args[1])
elif error == "StorageQuotaExceeded":
raise Repository.StorageQuotaExceeded(args[0], args[1])
else:
raise self.RPCError(unpacked)

View file

@ -1,19 +1,35 @@
import os
from ...constants import * # NOQA
from ...helpers import IncludePatternNeverMatchedWarning
from . import cmd_fixture, changedir # NOQA
from ...repository import Repository
from . import cmd, changedir, generate_archiver_tests # NOQA
pytest_generate_tests = lambda metafunc: generate_archiver_tests(metafunc, kinds="local,remote,binary") # NOQA
def test_return_codes(cmd_fixture, tmpdir):
repo = tmpdir / "repo" # borg creates the directory
input = tmpdir.mkdir("input")
output = tmpdir.mkdir("output")
input.join("test_file").write("content")
rc, out = cmd_fixture("--repo=%s" % str(repo), "repo-create", "--encryption=none")
assert rc == EXIT_SUCCESS
rc, out = cmd_fixture("--repo=%s" % repo, "create", "archive", str(input))
assert rc == EXIT_SUCCESS
with changedir(str(output)):
rc, out = cmd_fixture("--repo=%s" % repo, "extract", "archive")
assert rc == EXIT_SUCCESS
rc, out = cmd_fixture("--repo=%s" % repo, "extract", "archive", "does/not/match")
assert rc == IncludePatternNeverMatchedWarning().exit_code
def test_return_codes(archivers, request):
archiver = request.getfixturevalue(archivers)
cmd(archiver, "repo-create", "--encryption=none")
cmd(archiver, "create", "archive", "input")
with changedir("output"):
cmd(archiver, "extract", "archive")
cmd(
archiver,
"extract",
"archive",
"does/not/match",
fork=True,
exit_code=IncludePatternNeverMatchedWarning().exit_code,
)
def test_exit_codes(archivers, request, monkeypatch):
archiver = request.getfixturevalue(archivers)
# we create the repo path, but do NOT initialize the borg repo,
# so the borg create commands are expected to fail with DoesNotExist (was: InvalidRepository in borg 1.4).
os.makedirs(archiver.repository_path)
monkeypatch.setenv("BORG_EXIT_CODES", "classic")
cmd(archiver, "create", "archive", "input", fork=True, exit_code=EXIT_ERROR)
monkeypatch.setenv("BORG_EXIT_CODES", "modern")
cmd(archiver, "create", "archive", "input", fork=True, exit_code=Repository.DoesNotExist.exit_mcode)