Skip to content

Commit bb0a39a

Browse files
authored
feat(core): raise proper errors when Renku metadata is corrupt (#3393)
1 parent 32d6e55 commit bb0a39a

File tree

5 files changed

+61
-2
lines changed

5 files changed

+61
-2
lines changed

renku/core/errors.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -708,7 +708,19 @@ class NotebookSessionImageNotExistError(RenkuException):
708708

709709

710710
class MetadataMergeError(RenkuException):
711-
"""Raise when merging of metadata failed."""
711+
"""Raised when merging of metadata failed."""
712+
713+
714+
class MetadataCorruptError(RenkuException):
715+
"""Raised when metadata is corrupt and couldn't be loaded."""
716+
717+
def __init__(self, path: Union[str, Path]) -> None:
718+
message = f"Metadata file '{path}' couldn't be loaded because it is corrupted."
719+
with open(path) as f:
720+
content = f.read()
721+
if all(pattern in content for pattern in ["<<<<<<<", "=======", ">>>>>>>"]):
722+
message += "\nThis is likely due to an unresolved git merge conflict in the file."
723+
super().__init__(message)
712724

713725

714726
class MinimumVersionError(RenkuException):

renku/infrastructure/database.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,10 @@ def load(self, filename: str, absolute: bool = False):
833833
with self.zstd_decompressor.stream_reader(file) as zfile:
834834
data = json.load(zfile)
835835
else:
836-
data = json.load(file)
836+
try:
837+
data = json.load(file)
838+
except json.JSONDecodeError:
839+
raise errors.MetadataCorruptError(path)
837840
return data
838841

839842

renku/ui/service/errors.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,21 @@ def __init__(self, exception=None):
301301
super().__init__(exception=exception)
302302

303303

304+
class UserProjectMetadataCorruptError(ServiceError):
305+
"""The metadata in the project os corrupt and couldn't be loaded."""
306+
307+
code = SVC_ERROR_USER + 120
308+
userMessage = "The Renku metadata in the project is corrupt and couldn't be loaded: {error_message}"
309+
devMessage = "Couldn't parse project metadata due to a JSON parsing error: {error_message}"
310+
311+
def __init__(self, exception=None, error_message=ERROR_NOT_AVAILABLE):
312+
super().__init__(
313+
userMessage=self.userMessage.format(error_message=error_message),
314+
devMessage=self.devMessage.format(error_message=error_message),
315+
exception=exception,
316+
)
317+
318+
304319
class UserDatasetsMultipleImagesError(ServiceError):
305320
"""Multiple images dataset have the same priority."""
306321

renku/ui/service/views/error_handlers.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
GitCommandError,
3434
GitError,
3535
InvalidTemplateError,
36+
MetadataCorruptError,
3637
MigrationError,
3738
MigrationRequired,
3839
MinimumVersionError,
@@ -73,6 +74,7 @@
7374
UserNonRenkuProjectError,
7475
UserOutdatedProjectError,
7576
UserProjectCreationError,
77+
UserProjectMetadataCorruptError,
7678
UserProjectTemplateReferenceError,
7779
UserRepoBranchInvalidError,
7880
UserRepoNoAccessError,
@@ -146,6 +148,8 @@ def decorated_function(*args, **kwargs):
146148
"""Represents decorated function."""
147149
try:
148150
return f(*args, **kwargs)
151+
except MetadataCorruptError as e:
152+
raise UserProjectMetadataCorruptError(e, str(e))
149153
except MigrationRequired as e:
150154
raise UserOutdatedProjectError(e)
151155
except UninitializedProject as e:

tests/core/metadata/test_database.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from persistent.list import PersistentList
2222
from persistent.mapping import PersistentMapping
2323

24+
from renku.core import errors
2425
from renku.domain_model.entity import Entity
2526
from renku.domain_model.provenance.activity import Activity, Usage
2627
from renku.domain_model.workflow.plan import Plan
@@ -414,3 +415,27 @@ def test_database_immutable_object(database):
414415

415416
assert isinstance(usage_1, Usage)
416417
assert usage_1 is usage_2
418+
419+
420+
@pytest.mark.parametrize(
421+
"content,error_message",
422+
[
423+
("{'a':<' ' b}", ".*file' couldn't be loaded because it is corrupted.$"),
424+
(
425+
"{'name': \n<<<<<<< HEAD:file\n'a'\n=======\n'b'\n>>>>>>> abcdefg:file\n}",
426+
".*file' couldn't be loaded because it is corrupted.\n"
427+
"This is likely due to an unresolved git merge conflict in the file.$",
428+
),
429+
],
430+
)
431+
def test_database_corrupt_metadata(tmpdir, content, error_message):
432+
"""Test raising correct error for corrupt metadata."""
433+
from renku.infrastructure.database import Storage
434+
435+
storage = Storage(tmpdir)
436+
437+
with open(storage.path / "file", "w") as f:
438+
f.write(content)
439+
440+
with pytest.raises(expected_exception=errors.MetadataCorruptError, match=error_message):
441+
storage.load("file")

0 commit comments

Comments
 (0)