diff --git a/backend/api/urls.py b/backend/api/urls.py index 6cbc27751..093f26142 100644 --- a/backend/api/urls.py +++ b/backend/api/urls.py @@ -21,6 +21,7 @@ router.register(r"function", views.FunctionViewSet, basename="function") router.register(r"function", views.FunctionPermissionViewSet, basename="function") router.register(r"task", views.ComputeTaskViewSet, basename="task") +router.register(r"task", views.ComputeTaskPermissionViewSet, basename="task") router.register(r"compute_plan", views.ComputePlanViewSet, basename="compute_plan") router.register(r"compute_plan_metadata", views.ComputePlanMetadataViewSet, basename="compute_plan_metadata") router.register(r"news_feed", views.NewsFeedViewSet, basename="news_feed") diff --git a/backend/api/views/__init__.py b/backend/api/views/__init__.py index 25484555e..00fc5c50e 100644 --- a/backend/api/views/__init__.py +++ b/backend/api/views/__init__.py @@ -1,5 +1,6 @@ from .compute_plan_graph import get_cp_graph from .computeplan import ComputePlanViewSet +from .computetask import ComputeTaskPermissionViewSet from .computetask import ComputeTaskViewSet from .computetask import CPTaskViewSet from .datamanager import DataManagerPermissionViewSet @@ -28,6 +29,7 @@ "FunctionViewSet", "FunctionPermissionViewSet", "ComputeTaskViewSet", + "ComputeTaskPermissionViewSet", "ComputePlanViewSet", "CPTaskViewSet", "CPFunctionViewSet", diff --git a/backend/api/views/computetask.py b/backend/api/views/computetask.py index 25d24fc10..06da98c04 100644 --- a/backend/api/views/computetask.py +++ b/backend/api/views/computetask.py @@ -18,11 +18,13 @@ from rest_framework.viewsets import GenericViewSet from api.errors import AlreadyExistsError +from api.errors import AssetPermissionError from api.errors import BadRequestError from api.models import ComputePlan from api.models import ComputeTask from api.models import ComputeTaskInputAsset from api.models import ComputeTaskOutputAsset +from api.models.function import Function from api.models.function import FunctionInput from api.models.function import FunctionOutput from api.serializers import ComputeTaskInputAssetSerializer @@ -33,12 +35,15 @@ from api.views.filters_utils import MatchFilter from api.views.filters_utils import MetadataFilterBackend from api.views.utils import ApiResponse +from api.views.utils import PermissionMixin from api.views.utils import get_channel_name +from api.views.utils import get_file_response from api.views.utils import validate_key from api.views.utils import validate_metadata from libs.pagination import DefaultPageNumberPagination from orchestrator import computetask from orchestrator.resources import TAG_KEY +from substrapp.models import AssetFailureReport from substrapp.orchestrator import get_orchestrator_client logger = structlog.get_logger(__name__) @@ -280,6 +285,44 @@ def get_queryset(self): ) +class ComputeTaskPermissionViewSet(PermissionMixin, GenericViewSet): + queryset = ComputeTask.objects.all() + serializer_class = ComputeTaskSerializer + + @action(detail=True) + def logs(self, request, pk): + task = self.get_object() + channel_name = get_channel_name(request) + if task.error_type == ComputeTask.ErrorType.ERROR_TYPE_BUILD: + asset_class = Function + key = str(task.function.key) + else: + asset_class = ComputeTask + key = str(task.key) + + try: + asset = self.get_asset(request, key, channel_name, asset_class) + except AssetPermissionError as e: + return ApiResponse({"detail": str(e)}, status=status.HTTP_403_FORBIDDEN) + + url = task.logs_address + if not url: + return ApiResponse({"detail": "Asset not available anymore"}, status=status.HTTP_410_GONE) + + response = get_file_response( + local_file_class=AssetFailureReport, + key=key, + content_field="logs", + channel_name=channel_name, + url=url, + asset_owner=asset.get_owner(), + ) + + response.headers["Content-Type"] = "text/plain; charset=utf-8" + response.headers["Content-Disposition"] = f'attachment; filename="tuple_logs_{pk}.txt"' + return response + + class CPTaskViewSet(ComputeTaskViewSetConfig, mixins.ListModelMixin, GenericViewSet): serializer_class = ComputeTaskSerializer diff --git a/backend/api/views/failed_asset_logs.py b/backend/api/views/failed_asset_logs.py index 5dde40ba9..ac0c437ed 100644 --- a/backend/api/views/failed_asset_logs.py +++ b/backend/api/views/failed_asset_logs.py @@ -7,7 +7,6 @@ from api.models import ComputeTask from api.models import Function from api.views import utils as view_utils -from api.views.utils import ApiResponse from substrapp.models import asset_failure_report @@ -31,7 +30,7 @@ def file(self, request, pk=None) -> drf_response.Response: url = report.logs_address if not url: - return ApiResponse({"detail": "Asset not available anymore"}, status=status.HTTP_410_GONE) + return view_utils.ApiResponse({"detail": "Asset not available anymore"}, status=status.HTTP_410_GONE) response = view_utils.get_file_response( local_file_class=asset_failure_report.AssetFailureReport,