Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: store asset failure report supports exception without logs #970

Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions backend/builder/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from substrapp.compute_tasks.errors import CeleryNoRetryError
from substrapp.compute_tasks.errors import CeleryRetryError
from substrapp.compute_tasks.errors import ComputeTaskErrorType
from substrapp.compute_tasks.errors import _ComputeTaskError


class PodError(Exception):
Expand All @@ -14,7 +13,7 @@ class PodTimeoutError(Exception):
pass


class BuildRetryError(_ComputeTaskError, CeleryRetryError):
class _BuildError(RuntimeError):
SdgJlbl marked this conversation as resolved.
Show resolved Hide resolved
"""An error occurred during the build of a container image.

Args:
Expand All @@ -28,15 +27,17 @@ def __init__(self, logs: str, *args: list, **kwargs: dict):
super().__init__(logs, *args, **kwargs)


class BuildError(_ComputeTaskError, CeleryNoRetryError):
class BuildError(_BuildError, CeleryNoRetryError):
"""An error occurred during the build of a container image.

Args:
logs (str): the container image build logs
"""

error_type = ComputeTaskErrorType.BUILD_ERROR

def __init__(self, logs: str, *args: list, **kwargs: dict):
self.logs = BytesIO(str.encode(logs))
super().__init__(logs, *args, **kwargs)
class BuildRetryError(_BuildError, CeleryRetryError):
"""An error occurred during the build of a container image.

Args:
logs (str): the container image build logs
"""
18 changes: 0 additions & 18 deletions backend/substrapp/compute_tasks/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,24 +74,6 @@ def __init__(self, logs: BinaryIO, *args, **kwargs):
super().__init__(logs, *args, **kwargs)


def get_error_type(exc: Exception) -> failure_report_pb2.ErrorType.ValueType:
"""From a given exception, return an error type safe to store and to advertise to the user.

Args:
exc: The exception to process.

Returns:
The error type corresponding to the exception.
"""

if isinstance(exc, _ComputeTaskError):
error_type = exc.error_type
else:
error_type = ComputeTaskErrorType.INTERNAL_ERROR

return error_type.value


class InvalidContextError(_ComputeTaskError, CeleryNoRetryError):
"""Error due to invalid task Context"""

Expand Down
28 changes: 23 additions & 5 deletions backend/substrapp/tasks/tasks_asset_failure_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
from django.conf import settings

from backend.celery import app
from orchestrator import failure_report_pb2
from orchestrator import get_orchestrator_client
from substrapp.compute_tasks import errors as compute_task_errors
from substrapp.models import FailedAssetKind
from substrapp.utils.errors import store_failure

REGISTRY = settings.REGISTRY
Expand Down Expand Up @@ -45,10 +45,7 @@ def store_asset_failure_report(
) -> None:
exception = pickle.loads(exception_pickled) # nosec B301

if asset_type == FailedAssetKind.FAILED_ASSET_FUNCTION:
error_type = compute_task_errors.ComputeTaskErrorType.BUILD_ERROR.value
else:
error_type = compute_task_errors.get_error_type(exception)
error_type = get_error_type(exception)

failure_report = store_failure(exception, asset_key, asset_type, error_type)

Expand All @@ -66,3 +63,24 @@ def store_asset_failure_report(
client.register_failure_report(
{"asset_key": asset_key, "error_type": error_type, "asset_type": asset_type, "logs_address": logs_address}
)


def get_error_type(exc: Exception) -> failure_report_pb2.ErrorType.ValueType:
"""From a given exception, return an error type safe to store and to advertise to the user.

Args:
exc: The exception to process.

Returns:
The error type corresponding to the exception.
"""

if not hasattr(exc, "logs"):
return compute_task_errors.ComputeTaskErrorType.INTERNAL_ERROR.value

try:
error_type = exc.error_type
except AttributeError:
error_type = compute_task_errors.ComputeTaskErrorType.INTERNAL_ERROR

return error_type.value
16 changes: 0 additions & 16 deletions backend/substrapp/tests/compute_tasks/test_errors.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import io

import pytest

from builder import exceptions as build_errors
from orchestrator import failure_report_pb2
from substrapp.compute_tasks import errors


Expand All @@ -15,15 +11,3 @@ class TestComputeTaskErrorType:
def test_from_int(self, input_value: int, expected: errors.ComputeTaskErrorType):
result = errors.ComputeTaskErrorType.from_int(input_value)
assert result == expected


@pytest.mark.parametrize(
("exc", "expected"),
[
(build_errors.BuildError(logs="some build error"), failure_report_pb2.ERROR_TYPE_BUILD),
(errors.ExecutionError(logs=io.BytesIO()), failure_report_pb2.ERROR_TYPE_EXECUTION),
(Exception(), failure_report_pb2.ERROR_TYPE_INTERNAL),
],
)
def test_get_error_type(exc: Exception, expected: str):
assert errors.get_error_type(exc) == expected
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
import pytest
from pytest_mock import MockerFixture

from builder import exceptions as build_errors
from orchestrator import failure_report_pb2
from substrapp.compute_tasks import errors
from substrapp.compute_tasks.errors import ComputeTaskErrorType
from substrapp.models import FailedAssetKind
from substrapp.tasks.tasks_asset_failure_report import get_error_type
from substrapp.tasks.tasks_asset_failure_report import store_asset_failure_report
from substrapp.utils.errors import store_failure

Expand Down Expand Up @@ -67,3 +70,15 @@ def test_store_failure_ignored_exception(exc_class: Type[Exception]):
)
is None
)


@pytest.mark.parametrize(
("exc", "expected"),
[
(build_errors.BuildError(logs="some build error"), failure_report_pb2.ERROR_TYPE_BUILD),
(errors.ExecutionError(logs=io.BytesIO()), failure_report_pb2.ERROR_TYPE_EXECUTION),
(Exception(), failure_report_pb2.ERROR_TYPE_INTERNAL),
],
)
def test_get_error_type(exc: Exception, expected: str):
assert get_error_type(exc) == expected
1 change: 1 addition & 0 deletions changes/970.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Celery was failing silently when a task called `FailableTask.on_failure` if the task didn't have a `logs` attribute (now return internal error)
Loading