diff --git a/src/main/java/com/iexec/worker/chain/ContributionService.java b/src/main/java/com/iexec/worker/chain/ContributionService.java index 37d44cc7..76fb1925 100644 --- a/src/main/java/com/iexec/worker/chain/ContributionService.java +++ b/src/main/java/com/iexec/worker/chain/ContributionService.java @@ -16,12 +16,12 @@ package com.iexec.worker.chain; -import com.iexec.common.replicate.ReplicateStatusCause; import com.iexec.common.result.ComputedFile; import com.iexec.commons.poco.chain.*; import com.iexec.commons.poco.task.TaskDescription; import com.iexec.commons.poco.utils.BytesUtils; import com.iexec.commons.poco.utils.HashUtils; +import com.iexec.worker.workflow.WorkflowError; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.web3j.protocol.core.methods.response.Log; @@ -56,63 +56,63 @@ public boolean isChainTaskInitialized(String chainTaskId) { return iexecHubService.getTaskDescription(chainTaskId) != null; } - public List getCannotContributeStatusCause(final String chainTaskId) { - final List causes = new ArrayList<>(); + public List getCannotContributeStatusCause(final String chainTaskId) { + final List errors = new ArrayList<>(); if (!isWorkerpoolAuthorizationPresent(chainTaskId)) { - causes.add(WORKERPOOL_AUTHORIZATION_NOT_FOUND); + errors.add(new WorkflowError(WORKERPOOL_AUTHORIZATION_NOT_FOUND)); } final TaskDescription taskDescription = iexecHubService.getTaskDescription(chainTaskId); final ChainTask chainTask = iexecHubService.getChainTask(chainTaskId).orElse(null); if (chainTask == null) { - causes.add(CHAIN_UNREACHABLE); - return causes; + errors.add(new WorkflowError(CHAIN_UNREACHABLE)); + return errors; } // No staking in contributeAndFinalize if (taskDescription != null && !taskDescription.isEligibleToContributeAndFinalize() && !hasEnoughStakeToContribute(chainTask)) { - causes.add(STAKE_TOO_LOW); + errors.add(new WorkflowError(STAKE_TOO_LOW)); } if (chainTask.getStatus() != ChainTaskStatus.ACTIVE) { - causes.add(TASK_NOT_ACTIVE); + errors.add(new WorkflowError(TASK_NOT_ACTIVE)); } if (chainTask.isContributionDeadlineReached()) { - causes.add(CONTRIBUTION_TIMEOUT); + errors.add(new WorkflowError(CONTRIBUTION_TIMEOUT)); } if (chainTask.hasContributionFrom(workerWalletAddress)) { - causes.add(CONTRIBUTION_ALREADY_SET); + errors.add(new WorkflowError(CONTRIBUTION_ALREADY_SET)); } - return causes; + return errors; } - public List getCannotContributeAndFinalizeStatusCause(final String chainTaskId) { - final List causes = new ArrayList<>(); + public List getCannotContributeAndFinalizeStatusCause(final String chainTaskId) { + final List errors = new ArrayList<>(); // check TRUST is 1 final TaskDescription taskDescription = iexecHubService.getTaskDescription(chainTaskId); if (taskDescription == null || !BigInteger.ONE.equals(taskDescription.getTrust())) { - causes.add(TRUST_NOT_1); + errors.add(new WorkflowError(TRUST_NOT_1)); } final ChainTask chainTask = iexecHubService.getChainTask(chainTaskId).orElse(null); if (chainTask == null) { - causes.add(CHAIN_UNREACHABLE); - return causes; + errors.add(new WorkflowError(CHAIN_UNREACHABLE)); + return errors; } // check TASK_ALREADY_CONTRIBUTED if (chainTask.hasContributions()) { - causes.add(TASK_ALREADY_CONTRIBUTED); + errors.add(new WorkflowError(TASK_ALREADY_CONTRIBUTED)); } - return causes; + return errors; } private boolean isWorkerpoolAuthorizationPresent(String chainTaskId) { diff --git a/src/main/java/com/iexec/worker/compute/ComputeController.java b/src/main/java/com/iexec/worker/compute/ComputeController.java index c7ae145d..d639dd95 100644 --- a/src/main/java/com/iexec/worker/compute/ComputeController.java +++ b/src/main/java/com/iexec/worker/compute/ComputeController.java @@ -17,17 +17,18 @@ package com.iexec.worker.compute; -import com.iexec.common.replicate.ReplicateStatusCause; import com.iexec.common.result.ComputedFile; import com.iexec.common.worker.api.ExitMessage; import com.iexec.worker.chain.WorkerpoolAuthorizationService; import com.iexec.worker.result.ResultService; +import com.iexec.worker.workflow.WorkflowError; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.NoSuchElementException; +import java.util.Optional; import static org.springframework.http.ResponseEntity.ok; @@ -57,8 +58,11 @@ public ResponseEntity sendExitCauseForGivenComputeStage( @PathVariable ComputeStage stage, @PathVariable String chainTaskId, @RequestBody ExitMessage exitMessage) { - List causes = exitMessage != null && exitMessage.cause() != null ? List.of(exitMessage.cause()) : List.of(); - return sendExitCausesForGivenComputeStage(authorization, stage, chainTaskId, causes); + final List errors = Optional.ofNullable(exitMessage) + .map(ExitMessage::cause) + .map(WorkflowError::new) + .stream().toList(); + return sendExitCausesForGivenComputeStage(authorization, stage, chainTaskId, errors); } @PostMapping("/compute/{stage}/{chainTaskId}/exit-causes") @@ -66,7 +70,7 @@ public ResponseEntity sendExitCausesForGivenComputeStage( @RequestHeader("Authorization") String authorization, @PathVariable ComputeStage stage, @PathVariable String chainTaskId, - @RequestBody List causes) { + @RequestBody List errors) { try { if (!workerpoolAuthorizationService.isSignedWithEnclaveChallenge(chainTaskId, authorization)) { return ResponseEntity @@ -79,13 +83,13 @@ public ResponseEntity sendExitCausesForGivenComputeStage( .build(); } - if (causes == null || causes.isEmpty()) { + if (errors == null || errors.isEmpty()) { return ResponseEntity .status(HttpStatus.BAD_REQUEST.value()) .build(); } - final boolean stored = computeStageExitService.setExitCausesForGivenComputeStage(chainTaskId, stage, causes); + final boolean stored = computeStageExitService.setExitCausesForGivenComputeStage(chainTaskId, stage, errors); if (!stored) { return ResponseEntity diff --git a/src/main/java/com/iexec/worker/compute/ComputeExitCauseService.java b/src/main/java/com/iexec/worker/compute/ComputeExitCauseService.java index 39da0bd4..efeb7692 100644 --- a/src/main/java/com/iexec/worker/compute/ComputeExitCauseService.java +++ b/src/main/java/com/iexec/worker/compute/ComputeExitCauseService.java @@ -16,7 +16,7 @@ package com.iexec.worker.compute; -import com.iexec.common.replicate.ReplicateStatusCause; +import com.iexec.worker.workflow.WorkflowError; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -28,7 +28,7 @@ @Service public class ComputeExitCauseService { - private final HashMap> exitCauseMap = new HashMap<>(); + private final HashMap> exitCauseMap = new HashMap<>(); /** * Report failure exit causes from pre-compute or post-compute enclave. @@ -36,12 +36,12 @@ public class ComputeExitCauseService { * * @param computeStage pre-compute or post-compute-stage label * @param chainTaskId task ID - * @param causes list of root causes of the failure + * @param errors list of workflow errors describing the failure * @return true if exit causes are reported, false if already reported */ boolean setExitCausesForGivenComputeStage(final String chainTaskId, final ComputeStage computeStage, - final List causes) { + final List errors) { final String key = buildKey(computeStage, chainTaskId); if (exitCauseMap.containsKey(key)) { @@ -50,9 +50,9 @@ boolean setExitCausesForGivenComputeStage(final String chainTaskId, return false; } - exitCauseMap.put(key, List.copyOf(causes)); + exitCauseMap.put(key, List.copyOf(errors)); log.info("Added exit causes [chainTaskId:{}, computeStage:{}, causeCount:{}]", - chainTaskId, computeStage, causes.size()); + chainTaskId, computeStage, errors.size()); return true; } @@ -62,23 +62,23 @@ boolean setExitCausesForGivenComputeStage(final String chainTaskId, * * @param computeStage compute stage * @param chainTaskId task ID - * @param fallbackCause default cause to return if no specific causes are found - * @return list of exit causes, or default unknown issue if not found + * @param fallbackError default error to return if no specific causes are found + * @return list of workflow errors, or default unknown issue if not found */ - public List getExitCausesAndPruneForGivenComputeStage( + public List getExitCausesAndPruneForGivenComputeStage( final String chainTaskId, final ComputeStage computeStage, - final ReplicateStatusCause fallbackCause) { + final WorkflowError fallbackError) { final String key = buildKey(computeStage, chainTaskId); - final List causes = exitCauseMap.remove(key); - if (causes != null) { + final List errors = exitCauseMap.remove(key); + if (errors != null) { log.info("Retrieved and pruned exit causes [chainTaskId:{} computeStage:{}, causeCount:{}]", - chainTaskId, computeStage, causes.size()); - return causes; + chainTaskId, computeStage, errors.size()); + return errors; } else { log.info("No exit causes found, returning fallback cause [chainTaskId:{}, computeStage:{}]", chainTaskId, computeStage); - return List.of(fallbackCause); + return List.of(fallbackError); } } diff --git a/src/main/java/com/iexec/worker/compute/ComputeManagerService.java b/src/main/java/com/iexec/worker/compute/ComputeManagerService.java index 8c9b949a..4359f954 100644 --- a/src/main/java/com/iexec/worker/compute/ComputeManagerService.java +++ b/src/main/java/com/iexec/worker/compute/ComputeManagerService.java @@ -33,6 +33,7 @@ import com.iexec.worker.docker.DockerRegistryConfiguration; import com.iexec.worker.docker.DockerService; import com.iexec.worker.result.ResultService; +import com.iexec.worker.workflow.WorkflowError; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -230,7 +231,7 @@ public PostComputeResponse runPostCompute(final TaskDescription taskDescription, postComputeResponse = postComputeService.runTeePostCompute(taskDescription, secureSession); } else { postComputeResponse = PostComputeResponse.builder() - .exitCauses(List.of(ReplicateStatusCause.POST_COMPUTE_FAILED_UNKNOWN_ISSUE)) + .exitCauses(List.of(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_FAILED_UNKNOWN_ISSUE))) .build(); } if (!postComputeResponse.isSuccessful()) { @@ -239,7 +240,7 @@ public PostComputeResponse runPostCompute(final TaskDescription taskDescription, final ComputedFile computedFile = resultService.readComputedFile(chainTaskId); if (computedFile == null) { return PostComputeResponse.builder() - .exitCauses(List.of(ReplicateStatusCause.POST_COMPUTE_COMPUTED_FILE_NOT_FOUND)) + .exitCauses(List.of(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_COMPUTED_FILE_NOT_FOUND))) .stdout(postComputeResponse.getStdout()) .stderr(postComputeResponse.getStderr()) .build(); @@ -247,7 +248,7 @@ public PostComputeResponse runPostCompute(final TaskDescription taskDescription, final String resultDigest = resultService.computeResultDigest(computedFile); if (resultDigest.isEmpty()) { return PostComputeResponse.builder() - .exitCauses(List.of(ReplicateStatusCause.POST_COMPUTE_RESULT_DIGEST_COMPUTATION_FAILED)) + .exitCauses(List.of(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_RESULT_DIGEST_COMPUTATION_FAILED))) .stdout(postComputeResponse.getStdout()) .stderr(postComputeResponse.getStderr()) .build(); diff --git a/src/main/java/com/iexec/worker/compute/ComputeResponse.java b/src/main/java/com/iexec/worker/compute/ComputeResponse.java index aa622a1f..b5832487 100644 --- a/src/main/java/com/iexec/worker/compute/ComputeResponse.java +++ b/src/main/java/com/iexec/worker/compute/ComputeResponse.java @@ -16,13 +16,13 @@ package com.iexec.worker.compute; -import com.iexec.common.replicate.ReplicateStatusCause; +import com.iexec.worker.workflow.WorkflowError; import java.util.List; public interface ComputeResponse { - List getExitCauses(); + List getExitCauses(); String getStdout(); diff --git a/src/main/java/com/iexec/worker/compute/app/AppComputeResponse.java b/src/main/java/com/iexec/worker/compute/app/AppComputeResponse.java index f50d64f6..571e4b53 100644 --- a/src/main/java/com/iexec/worker/compute/app/AppComputeResponse.java +++ b/src/main/java/com/iexec/worker/compute/app/AppComputeResponse.java @@ -16,8 +16,8 @@ package com.iexec.worker.compute.app; -import com.iexec.common.replicate.ReplicateStatusCause; import com.iexec.worker.compute.ComputeResponse; +import com.iexec.worker.workflow.WorkflowError; import lombok.Builder; import lombok.Value; @@ -28,7 +28,7 @@ public class AppComputeResponse implements ComputeResponse { @Builder.Default - List exitCauses = List.of(); + List exitCauses = List.of(); String stdout; String stderr; int exitCode; diff --git a/src/main/java/com/iexec/worker/compute/app/AppComputeService.java b/src/main/java/com/iexec/worker/compute/app/AppComputeService.java index 1ae8cd14..0f5925bb 100644 --- a/src/main/java/com/iexec/worker/compute/app/AppComputeService.java +++ b/src/main/java/com/iexec/worker/compute/app/AppComputeService.java @@ -32,6 +32,7 @@ import com.iexec.worker.sgx.SgxService; import com.iexec.worker.tee.TeeService; import com.iexec.worker.tee.TeeServicesManager; +import com.iexec.worker.workflow.WorkflowError; import org.springframework.stereotype.Service; import java.time.Duration; @@ -119,10 +120,10 @@ private String getTaskContainerName(final String chainTaskId) { return workerConfigService.getWorkerName() + "-" + chainTaskId; } - private List getExitCauseFromFinalStatus(final DockerRunFinalStatus finalStatus) { + private List getExitCauseFromFinalStatus(final DockerRunFinalStatus finalStatus) { return switch (finalStatus) { - case TIMEOUT -> List.of(ReplicateStatusCause.APP_COMPUTE_TIMEOUT); - case FAILED -> List.of(ReplicateStatusCause.APP_COMPUTE_FAILED); + case TIMEOUT -> List.of(new WorkflowError(ReplicateStatusCause.APP_COMPUTE_TIMEOUT)); + case FAILED -> List.of(new WorkflowError(ReplicateStatusCause.APP_COMPUTE_FAILED)); default -> List.of(); }; } diff --git a/src/main/java/com/iexec/worker/compute/post/PostComputeResponse.java b/src/main/java/com/iexec/worker/compute/post/PostComputeResponse.java index beb1b14c..d3d54f85 100644 --- a/src/main/java/com/iexec/worker/compute/post/PostComputeResponse.java +++ b/src/main/java/com/iexec/worker/compute/post/PostComputeResponse.java @@ -16,8 +16,8 @@ package com.iexec.worker.compute.post; -import com.iexec.common.replicate.ReplicateStatusCause; import com.iexec.worker.compute.ComputeResponse; +import com.iexec.worker.workflow.WorkflowError; import lombok.Builder; import lombok.Value; @@ -28,7 +28,7 @@ public class PostComputeResponse implements ComputeResponse { @Builder.Default - List exitCauses = List.of(); + List exitCauses = List.of(); String stdout; String stderr; } diff --git a/src/main/java/com/iexec/worker/compute/post/PostComputeService.java b/src/main/java/com/iexec/worker/compute/post/PostComputeService.java index 8d35022d..bf31dac2 100644 --- a/src/main/java/com/iexec/worker/compute/post/PostComputeService.java +++ b/src/main/java/com/iexec/worker/compute/post/PostComputeService.java @@ -38,6 +38,7 @@ import com.iexec.worker.tee.TeeService; import com.iexec.worker.tee.TeeServicesManager; import com.iexec.worker.tee.TeeServicesPropertiesService; +import com.iexec.worker.workflow.WorkflowError; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -52,10 +53,6 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Stream; -import static com.iexec.common.replicate.ReplicateStatusCause.POST_COMPUTE_FAILED_UNKNOWN_ISSUE; -import static com.iexec.common.replicate.ReplicateStatusCause.POST_COMPUTE_TOO_LONG_RESULT_FILE_NAME; - - @Slf4j @Service public class PostComputeService { @@ -111,7 +108,7 @@ public PostComputeResponse runStandardPostCompute(TaskDescription taskDescriptio final Optional resultFilesNameError = checkResultFilesName(chainTaskId, taskIexecOutDir); if (resultFilesNameError.isPresent()) { return PostComputeResponse.builder() - .exitCauses(List.of(resultFilesNameError.get())) + .exitCauses(List.of(new WorkflowError(resultFilesNameError.get()))) .build(); } @@ -122,7 +119,7 @@ public PostComputeResponse runStandardPostCompute(TaskDescription taskDescriptio taskOutputDir); if (zipIexecOutPath.isEmpty()) { return PostComputeResponse.builder() - .exitCauses(List.of(ReplicateStatusCause.POST_COMPUTE_OUT_FOLDER_ZIP_FAILED)) + .exitCauses(List.of(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_OUT_FOLDER_ZIP_FAILED))) .build(); } // copy /output/iexec_out/computed.json to /output/computed.json as in FlowService#sendComputedFileToHost @@ -133,7 +130,7 @@ public PostComputeResponse runStandardPostCompute(TaskDescription taskDescriptio if (!isCopied) { log.error("Failed to copy computed.json file to /output [chainTaskId:{}]", chainTaskId); return PostComputeResponse.builder() - .exitCauses(List.of(ReplicateStatusCause.POST_COMPUTE_SEND_COMPUTED_FILE_FAILED)) + .exitCauses(List.of(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_SEND_COMPUTED_FILE_FAILED))) .build(); } return PostComputeResponse.builder().build(); @@ -155,11 +152,11 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { }); } catch (IOException e) { log.error("Can't check result files [chainTaskId:{}]", taskId); - return Optional.of(POST_COMPUTE_FAILED_UNKNOWN_ISSUE); + return Optional.of(ReplicateStatusCause.POST_COMPUTE_FAILED_UNKNOWN_ISSUE); } if (failed.get()) { - return Optional.of(POST_COMPUTE_TOO_LONG_RESULT_FILE_NAME); + return Optional.of(ReplicateStatusCause.POST_COMPUTE_TOO_LONG_RESULT_FILE_NAME); } return Optional.empty(); } @@ -177,7 +174,7 @@ public PostComputeResponse runTeePostCompute(TaskDescription taskDescription, log.error("Tee post-compute image not found locally [chainTaskId:{}]", chainTaskId); return PostComputeResponse.builder() - .exitCauses(List.of(ReplicateStatusCause.POST_COMPUTE_IMAGE_MISSING)) + .exitCauses(List.of(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_IMAGE_MISSING))) .build(); } TeeService teeService = teeServicesManager.getTeeService(taskDescription.getTeeFramework()); @@ -214,12 +211,12 @@ public PostComputeResponse runTeePostCompute(TaskDescription taskDescription, log.error("Tee post-compute container timed out [chainTaskId:{}, maxExecutionTime:{}]", chainTaskId, taskDescription.getMaxExecutionTime()); return PostComputeResponse.builder() - .exitCauses(List.of(ReplicateStatusCause.POST_COMPUTE_TIMEOUT)) + .exitCauses(List.of(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_TIMEOUT))) .build(); } if (finalStatus == DockerRunFinalStatus.FAILED) { final int exitCode = dockerResponse.getContainerExitCode(); - final List exitCauses = getExitCauses(chainTaskId, exitCode); + final List exitCauses = getExitCauses(chainTaskId, exitCode); log.error("Failed to run tee post-compute [chainTaskId:{}, exitCode:{}, exitCauses:{}]", chainTaskId, exitCode, exitCauses); return PostComputeResponse.builder() @@ -232,14 +229,14 @@ public PostComputeResponse runTeePostCompute(TaskDescription taskDescription, .build(); } - private List getExitCauses(final String chainTaskId, final int exitCode) { + private List getExitCauses(final String chainTaskId, final int exitCode) { return switch (exitCode) { case 0 -> List.of(); - case 1 -> - computeExitCauseService.getExitCausesAndPruneForGivenComputeStage(chainTaskId, ComputeStage.POST, POST_COMPUTE_FAILED_UNKNOWN_ISSUE); - case 2 -> List.of(ReplicateStatusCause.POST_COMPUTE_EXIT_REPORTING_FAILED); - case 3 -> List.of(ReplicateStatusCause.POST_COMPUTE_TASK_ID_MISSING); - default -> List.of(POST_COMPUTE_FAILED_UNKNOWN_ISSUE); + case 1 -> computeExitCauseService.getExitCausesAndPruneForGivenComputeStage( + chainTaskId, ComputeStage.POST, new WorkflowError(ReplicateStatusCause.POST_COMPUTE_FAILED_UNKNOWN_ISSUE)); + case 2 -> List.of(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_EXIT_REPORTING_FAILED)); + case 3 -> List.of(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_TASK_ID_MISSING)); + default -> List.of(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_FAILED_UNKNOWN_ISSUE)); }; } diff --git a/src/main/java/com/iexec/worker/compute/pre/PreComputeResponse.java b/src/main/java/com/iexec/worker/compute/pre/PreComputeResponse.java index b9ac092b..1cc75db5 100644 --- a/src/main/java/com/iexec/worker/compute/pre/PreComputeResponse.java +++ b/src/main/java/com/iexec/worker/compute/pre/PreComputeResponse.java @@ -16,9 +16,9 @@ package com.iexec.worker.compute.pre; -import com.iexec.common.replicate.ReplicateStatusCause; import com.iexec.sms.api.TeeSessionGenerationResponse; import com.iexec.worker.compute.ComputeResponse; +import com.iexec.worker.workflow.WorkflowError; import lombok.Builder; import lombok.Value; @@ -29,7 +29,7 @@ public class PreComputeResponse implements ComputeResponse { @Builder.Default - List exitCauses = List.of(); + List exitCauses = List.of(); boolean isTeeTask; TeeSessionGenerationResponse secureSession; String stdout; diff --git a/src/main/java/com/iexec/worker/compute/pre/PreComputeService.java b/src/main/java/com/iexec/worker/compute/pre/PreComputeService.java index 534db3d8..5916e946 100644 --- a/src/main/java/com/iexec/worker/compute/pre/PreComputeService.java +++ b/src/main/java/com/iexec/worker/compute/pre/PreComputeService.java @@ -39,6 +39,7 @@ import com.iexec.worker.sms.TeeSessionGenerationException; import com.iexec.worker.tee.TeeServicesManager; import com.iexec.worker.tee.TeeServicesPropertiesService; +import com.iexec.worker.workflow.WorkflowError; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.util.unit.DataSize; @@ -48,7 +49,6 @@ import java.util.List; import java.util.concurrent.TimeoutException; -import static com.iexec.common.replicate.ReplicateStatusCause.*; import static com.iexec.sms.api.TeeSessionGenerationError.UNKNOWN_ISSUE; @Slf4j @@ -103,14 +103,14 @@ public PreComputeResponse runTeePreCompute(TaskDescription taskDescription, Work if (enclaveConfig == null) { log.error("No enclave configuration found for task [chainTaskId:{}]", chainTaskId); return preComputeResponseBuilder - .exitCauses(List.of(PRE_COMPUTE_MISSING_ENCLAVE_CONFIGURATION)) + .exitCauses(List.of(new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_MISSING_ENCLAVE_CONFIGURATION))) .build(); } if (!enclaveConfig.getValidator().isValid()) { log.error("Invalid enclave configuration [chainTaskId:{}, violations:{}]", chainTaskId, enclaveConfig.getValidator().validate().toString()); return preComputeResponseBuilder - .exitCauses(List.of(PRE_COMPUTE_INVALID_ENCLAVE_CONFIGURATION)) + .exitCauses(List.of(new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_INVALID_ENCLAVE_CONFIGURATION))) .build(); } long teeComputeMaxHeapSize = DataSize @@ -119,7 +119,7 @@ public PreComputeResponse runTeePreCompute(TaskDescription taskDescription, Work if (enclaveConfig.getHeapSize() > teeComputeMaxHeapSize) { log.error("Enclave configuration should define a proper heap size [chainTaskId:{}, heapSize:{}, maxHeapSize:{}]", chainTaskId, enclaveConfig.getHeapSize(), teeComputeMaxHeapSize); - preComputeResponseBuilder.exitCauses(List.of(PRE_COMPUTE_INVALID_ENCLAVE_HEAP_CONFIGURATION)); + preComputeResponseBuilder.exitCauses(List.of(new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_INVALID_ENCLAVE_HEAP_CONFIGURATION))); return preComputeResponseBuilder.build(); } // create secure session @@ -133,7 +133,7 @@ public PreComputeResponse runTeePreCompute(TaskDescription taskDescription, Work } catch (TeeSessionGenerationException e) { log.error("Failed to create TEE secure session [chainTaskId:{}]", chainTaskId, e); return preComputeResponseBuilder - .exitCauses(List.of(teeSessionGenerationErrorToReplicateStatusCause(e.getTeeSessionGenerationError()))) + .exitCauses(List.of(new WorkflowError(teeSessionGenerationErrorToReplicateStatusCause(e.getTeeSessionGenerationError())))) .build(); } @@ -141,41 +141,41 @@ public PreComputeResponse runTeePreCompute(TaskDescription taskDescription, Work if (taskDescription.requiresPreCompute()) { log.info("Task contains TEE input data [chainTaskId:{}, containsDataset:{}, containsInputFiles:{}, isBulkRequest:{}]", chainTaskId, taskDescription.containsDataset(), taskDescription.containsInputFiles(), taskDescription.isBulkRequest()); - final List exitCauses = downloadDatasetAndFiles(taskDescription, secureSession); + final List exitCauses = downloadDatasetAndFiles(taskDescription, secureSession); preComputeResponseBuilder.exitCauses(exitCauses); } return preComputeResponseBuilder.build(); } - private List downloadDatasetAndFiles( + private List downloadDatasetAndFiles( final TaskDescription taskDescription, final TeeSessionGenerationResponse secureSession) { try { final Integer exitCode = prepareTeeInputData(taskDescription, secureSession); if (exitCode == null || exitCode != 0) { final String chainTaskId = taskDescription.getChainTaskId(); - final List exitCauses = getExitCauses(chainTaskId, exitCode); + final List exitCauses = getExitCauses(chainTaskId, exitCode); log.error("Failed to prepare TEE input data [chainTaskId:{}, exitCode:{}, exitCauses:{}]", chainTaskId, exitCode, exitCauses); return exitCauses; } } catch (TimeoutException e) { - return List.of(PRE_COMPUTE_TIMEOUT); + return List.of(new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_TIMEOUT)); } return List.of(); } - private List getExitCauses(final String chainTaskId, final Integer exitCode) { + private List getExitCauses(final String chainTaskId, final Integer exitCode) { if (exitCode == null) { - return List.of(PRE_COMPUTE_IMAGE_MISSING); + return List.of(new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_IMAGE_MISSING)); } return switch (exitCode) { case 1 -> computeExitCauseService.getExitCausesAndPruneForGivenComputeStage( - chainTaskId, ComputeStage.PRE, PRE_COMPUTE_FAILED_UNKNOWN_ISSUE); - case 2 -> List.of(PRE_COMPUTE_EXIT_REPORTING_FAILED); - case 3 -> List.of(PRE_COMPUTE_TASK_ID_MISSING); - default -> List.of(PRE_COMPUTE_FAILED_UNKNOWN_ISSUE); + chainTaskId, ComputeStage.PRE, new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_FAILED_UNKNOWN_ISSUE)); + case 2 -> List.of(new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_EXIT_REPORTING_FAILED)); + case 3 -> List.of(new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_TASK_ID_MISSING)); + default -> List.of(new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_FAILED_UNKNOWN_ISSUE)); }; } diff --git a/src/main/java/com/iexec/worker/dataset/DataService.java b/src/main/java/com/iexec/worker/dataset/DataService.java index 586c1d4e..76a3a2da 100644 --- a/src/main/java/com/iexec/worker/dataset/DataService.java +++ b/src/main/java/com/iexec/worker/dataset/DataService.java @@ -23,7 +23,7 @@ import com.iexec.commons.poco.task.TaskDescription; import com.iexec.commons.poco.utils.MultiAddressHelper; import com.iexec.worker.config.WorkerConfigurationService; -import com.iexec.worker.utils.WorkflowException; +import com.iexec.worker.workflow.WorkflowException; import jakarta.annotation.Nonnull; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; diff --git a/src/main/java/com/iexec/worker/result/ResultService.java b/src/main/java/com/iexec/worker/result/ResultService.java index e5d6ee2d..2ff95372 100644 --- a/src/main/java/com/iexec/worker/result/ResultService.java +++ b/src/main/java/com/iexec/worker/result/ResultService.java @@ -21,7 +21,6 @@ import com.iexec.common.lifecycle.purge.ExpiringTaskMapFactory; import com.iexec.common.lifecycle.purge.Purgeable; import com.iexec.common.replicate.ReplicateStatus; -import com.iexec.common.replicate.ReplicateStatusCause; import com.iexec.common.result.ComputedFile; import com.iexec.common.result.ResultModel; import com.iexec.common.utils.FileHelper; @@ -34,6 +33,7 @@ import com.iexec.worker.chain.IexecHubService; import com.iexec.worker.config.PublicConfigurationService; import com.iexec.worker.config.WorkerConfigurationService; +import com.iexec.worker.workflow.WorkflowError; import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -93,7 +93,7 @@ public boolean isResultZipFound(final String chainTaskId) { } public boolean writeErrorToIexecOut(final String chainTaskId, final ReplicateStatus errorStatus, - final List causes) { + final List causes) { if (causes == null || causes.isEmpty()) { log.error("No error causes provided [chainTaskId:{}]", chainTaskId); return false; diff --git a/src/main/java/com/iexec/worker/task/TaskManagerService.java b/src/main/java/com/iexec/worker/task/TaskManagerService.java index 1727813e..9a3d05f0 100644 --- a/src/main/java/com/iexec/worker/task/TaskManagerService.java +++ b/src/main/java/com/iexec/worker/task/TaskManagerService.java @@ -41,7 +41,8 @@ import com.iexec.worker.tee.TeeService; import com.iexec.worker.tee.TeeServicesManager; import com.iexec.worker.utils.LoggingUtils; -import com.iexec.worker.utils.WorkflowException; +import com.iexec.worker.workflow.WorkflowError; +import com.iexec.worker.workflow.WorkflowException; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -96,16 +97,16 @@ public TaskManagerService( ReplicateActionResponse start(final TaskDescription taskDescription) { final String chainTaskId = taskDescription.getChainTaskId(); - final List causes = contributionService.getCannotContributeStatusCause(chainTaskId); + final List errors = contributionService.getCannotContributeStatusCause(chainTaskId); final String context = "start"; - if (!causes.isEmpty()) { - return getFailureResponseAndPrintErrors(causes, context, chainTaskId); + if (!errors.isEmpty()) { + return getFailureResponseAndPrintErrors(errors, context, chainTaskId); } // result encryption is not supported for standard tasks if (!taskDescription.isTeeTask() && taskDescription.getDealParams().isIexecResultEncryption()) { return getFailureResponseAndPrintErrors( - List.of(TASK_DESCRIPTION_INVALID), context, chainTaskId); + List.of(new WorkflowError(TASK_DESCRIPTION_INVALID)), context, chainTaskId); } if (taskDescription.isTeeTask()) { @@ -113,7 +114,7 @@ ReplicateActionResponse start(final TaskDescription taskDescription) { // then we won't be able to run the task. // So it should be aborted right now. final TeeService teeService = teeServicesManager.getTeeService(taskDescription.getTeeFramework()); - final List teePrerequisitesIssues = teeService.areTeePrerequisitesMetForTask(chainTaskId); + final List teePrerequisitesIssues = teeService.areTeePrerequisitesMetForTask(chainTaskId); if (!teePrerequisitesIssues.isEmpty()) { log.error("TEE prerequisites are not met [chainTaskId:{}, issues:{}]", chainTaskId, teePrerequisitesIssues); return getFailureResponseAndPrintErrors(teePrerequisitesIssues, context, chainTaskId); @@ -130,17 +131,17 @@ ReplicateActionResponse start(final TaskDescription taskDescription) { ReplicateActionResponse downloadApp(final TaskDescription taskDescription) { final String chainTaskId = taskDescription.getChainTaskId(); - final List causes = contributionService.getCannotContributeStatusCause(chainTaskId); + final List errors = contributionService.getCannotContributeStatusCause(chainTaskId); final String context = "download app"; - if (!causes.isEmpty()) { - return getFailureResponseAndPrintErrors(causes, context, chainTaskId); + if (!errors.isEmpty()) { + return getFailureResponseAndPrintErrors(errors, context, chainTaskId); } if (computeManagerService.downloadApp(taskDescription)) { return ReplicateActionResponse.success(); } return triggerPostComputeHookOnError( - chainTaskId, context, taskDescription, APP_DOWNLOAD_FAILED, List.of(APP_IMAGE_DOWNLOAD_FAILED)); + chainTaskId, context, taskDescription, APP_DOWNLOAD_FAILED, List.of(new WorkflowError(APP_IMAGE_DOWNLOAD_FAILED))); } /* @@ -171,10 +172,10 @@ ReplicateActionResponse downloadData(final TaskDescription taskDescription) { log.info("Dataset and input files will be downloaded by the pre-compute enclave [chainTaskId:{}]", chainTaskId); return ReplicateActionResponse.success(); } - final List causes = contributionService.getCannotContributeStatusCause(chainTaskId); + final List errors = contributionService.getCannotContributeStatusCause(chainTaskId); final String context = "download data"; - if (!causes.isEmpty()) { - return getFailureResponseAndPrintErrors(causes, context, chainTaskId); + if (!errors.isEmpty()) { + return getFailureResponseAndPrintErrors(errors, context, chainTaskId); } try { // download dataset for standard task @@ -192,23 +193,23 @@ ReplicateActionResponse downloadData(final TaskDescription taskDescription) { } } catch (WorkflowException e) { return triggerPostComputeHookOnError( - chainTaskId, context, taskDescription, DATA_DOWNLOAD_FAILED, List.of(e.getReplicateStatusCause())); + chainTaskId, context, taskDescription, DATA_DOWNLOAD_FAILED, List.of(new WorkflowError(e.getReplicateStatusCause()))); } return ReplicateActionResponse.success(); } - private ReplicateActionResponse triggerPostComputeHookOnError(String chainTaskId, - String context, - TaskDescription taskDescription, - ReplicateStatus errorStatus, - List causes) { + private ReplicateActionResponse triggerPostComputeHookOnError(final String chainTaskId, + final String context, + final TaskDescription taskDescription, + final ReplicateStatus errorStatus, + final List errors) { // log original errors - causes.forEach(cause -> logError(cause, context, chainTaskId)); - boolean isOk = resultService.writeErrorToIexecOut(chainTaskId, errorStatus, causes); + errors.forEach(error -> logError(error.cause(), context, chainTaskId)); + final boolean isOk = resultService.writeErrorToIexecOut(chainTaskId, errorStatus, errors); // try to run post-compute if (isOk && computeManagerService.runPostCompute(taskDescription, null).isSuccessful()) { //Graceful error, worker will be prompt to contribute - return ReplicateActionResponse.failure(causes.get(0)); + return ReplicateActionResponse.failure(errors.get(0).cause()); } //Download failed hard, worker cannot contribute logError(POST_COMPUTE_FAILED_UNKNOWN_ISSUE, context, chainTaskId); @@ -217,22 +218,22 @@ private ReplicateActionResponse triggerPostComputeHookOnError(String chainTaskId ReplicateActionResponse compute(final TaskDescription taskDescription) { final String chainTaskId = taskDescription.getChainTaskId(); - final List causes = contributionService.getCannotContributeStatusCause(chainTaskId); final String context = "compute"; - if (!causes.isEmpty()) { - return getFailureResponseAndPrintErrors(causes, context, chainTaskId); + final List errors = contributionService.getCannotContributeStatusCause(chainTaskId); + if (!errors.isEmpty()) { + return getFailureResponseAndPrintErrors(errors, context, chainTaskId); } if (!computeManagerService.isAppDownloaded(taskDescription.getAppUri())) { return getFailureResponseAndPrintErrors( - List.of(APP_NOT_FOUND_LOCALLY), context, chainTaskId); + List.of(new WorkflowError(APP_NOT_FOUND_LOCALLY)), context, chainTaskId); } if (taskDescription.isTeeTask()) { - TeeService teeService = teeServicesManager.getTeeService(taskDescription.getTeeFramework()); + final TeeService teeService = teeServicesManager.getTeeService(taskDescription.getTeeFramework()); if (!teeService.prepareTeeForTask(chainTaskId)) { return getFailureResponseAndPrintErrors( - List.of(TEE_PREPARATION_FAILED), context, chainTaskId); + List.of(new WorkflowError(TEE_PREPARATION_FAILED)), context, chainTaskId); } } @@ -245,11 +246,11 @@ ReplicateActionResponse compute(final TaskDescription taskDescription) { final AppComputeResponse appResponse = computeManagerService.runCompute(taskDescription, preResponse.getSecureSession()); if (!appResponse.isSuccessful()) { - final List appErrorCauses = appResponse.getExitCauses(); - appErrorCauses.forEach(cause -> logError(cause, context, chainTaskId)); + final List appErrors = appResponse.getExitCauses(); + appErrors.forEach(error -> logError(error.cause(), context, chainTaskId)); return ReplicateActionResponse.failureWithDetails( ReplicateStatusDetails.builder() - .cause(appErrorCauses.get(0)) //TODO: Handle list of causes + .cause(appErrors.get(0).cause()) //TODO: Handle list of causes .exitCode(appResponse.getExitCode()) .computeLogs( ComputeLogs.builder() @@ -262,9 +263,9 @@ ReplicateActionResponse compute(final TaskDescription taskDescription) { final PostComputeResponse postResponse = computeManagerService.runPostCompute(taskDescription, preResponse.getSecureSession()); if (!postResponse.isSuccessful()) { - final List postComputeErrorCauses = postResponse.getExitCauses(); - postComputeErrorCauses.forEach(cause -> logError(cause, context, chainTaskId)); - return ReplicateActionResponse.failureWithStdout(postComputeErrorCauses.get(0), postResponse.getStdout()); // TODO: Handle list of causes + final List postComputeErrors = postResponse.getExitCauses(); + postComputeErrors.forEach(error -> logError(error.cause(), context, chainTaskId)); + return ReplicateActionResponse.failureWithStdout(postComputeErrors.get(0).cause(), postResponse.getStdout()); // TODO: Handle list of causes } return ReplicateActionResponse.successWithLogs( ComputeLogs.builder() @@ -285,23 +286,23 @@ ReplicateActionResponse compute(final TaskDescription taskDescription) { * @return The response of the 'contribute' or 'contributeAndFinalize' action */ private ReplicateActionResponse contributeOrContributeAndFinalize(final String chainTaskId, final String context) { - final List causes = contributionService.getCannotContributeStatusCause(chainTaskId); - if (!causes.isEmpty()) { - return getFailureResponseAndPrintErrors(causes, context, chainTaskId); + final List errors = contributionService.getCannotContributeStatusCause(chainTaskId); + if (!errors.isEmpty()) { + return getFailureResponseAndPrintErrors(errors, context, chainTaskId); } if (!hasEnoughGas()) { return getFailureResponseAndPrintErrors( - List.of(OUT_OF_GAS), context, chainTaskId); + List.of(new WorkflowError(OUT_OF_GAS)), context, chainTaskId); } - ComputedFile computedFile = resultService.getComputedFile(chainTaskId); + final ComputedFile computedFile = resultService.getComputedFile(chainTaskId); if (computedFile == null) { logError("computed file error", context, chainTaskId); return ReplicateActionResponse.failure(DETERMINISM_HASH_NOT_FOUND); } - Contribution contribution = contributionService.getContribution(computedFile); + final Contribution contribution = contributionService.getContribution(computedFile); if (contribution == null) { logError("get contribution error", context, chainTaskId); return ReplicateActionResponse.failure(ENCLAVE_SIGNATURE_NOT_FOUND);//TODO update status @@ -316,14 +317,14 @@ private ReplicateActionResponse contributeOrContributeAndFinalize(final String c response = ReplicateActionResponse.success(chainReceipt); } } else if (context.equals(CONTRIBUTE_AND_FINALIZE)) { - causes.addAll(contributionService.getCannotContributeAndFinalizeStatusCause(chainTaskId)); - if (!causes.isEmpty()) { - return getFailureResponseAndPrintErrors(causes, context, chainTaskId); + errors.addAll(contributionService.getCannotContributeAndFinalizeStatusCause(chainTaskId)); + if (!errors.isEmpty()) { + return getFailureResponseAndPrintErrors(errors, context, chainTaskId); } final WorkerpoolAuthorization workerpoolAuthorization = contributionService.getWorkerpoolAuthorization(chainTaskId); - String callbackData = computedFile.getCallbackData(); - String resultLink = resultService.uploadResultAndGetLink(workerpoolAuthorization); + final String callbackData = computedFile.getCallbackData(); + final String resultLink = resultService.uploadResultAndGetLink(workerpoolAuthorization); log.debug("contributeAndFinalize [contribution:{}, resultLink:{}, callbackData:{}]", contribution, resultLink, callbackData); @@ -343,16 +344,16 @@ private ReplicateActionResponse contributeOrContributeAndFinalize(final String c return response; } - ReplicateActionResponse contribute(String chainTaskId) { + ReplicateActionResponse contribute(final String chainTaskId) { return contributeOrContributeAndFinalize(chainTaskId, CONTRIBUTE); } - ReplicateActionResponse reveal(String chainTaskId, - TaskNotificationExtra extra) { + ReplicateActionResponse reveal(final String chainTaskId, + final TaskNotificationExtra extra) { final String context = "reveal"; if (extra == null || extra.getBlockNumber() == 0) { return getFailureResponseAndPrintErrors( - List.of(CONSENSUS_BLOCK_MISSING), context, chainTaskId); + List.of(new WorkflowError(CONSENSUS_BLOCK_MISSING)), context, chainTaskId); } long consensusBlock = extra.getBlockNumber(); @@ -366,12 +367,12 @@ ReplicateActionResponse reveal(String chainTaskId, if (!revealService.isConsensusBlockReached(chainTaskId, consensusBlock)) { return getFailureResponseAndPrintErrors( - List.of(BLOCK_NOT_REACHED), context, chainTaskId); + List.of(new WorkflowError(BLOCK_NOT_REACHED)), context, chainTaskId); } if (!revealService.repeatCanReveal(chainTaskId, resultDigest)) { return getFailureResponseAndPrintErrors( - List.of(CANNOT_REVEAL), context, chainTaskId); + List.of(new WorkflowError(CANNOT_REVEAL)), context, chainTaskId); } if (!hasEnoughGas()) { @@ -380,28 +381,28 @@ ReplicateActionResponse reveal(String chainTaskId, System.exit(0); } - Optional oChainReceipt = + final Optional oChainReceipt = revealService.reveal(chainTaskId, resultDigest); if (oChainReceipt.isEmpty() || !isValidChainReceipt(chainTaskId, oChainReceipt.get())) { return getFailureResponseAndPrintErrors( - List.of(CHAIN_RECEIPT_NOT_VALID), context, chainTaskId); + List.of(new WorkflowError(CHAIN_RECEIPT_NOT_VALID)), context, chainTaskId); } return ReplicateActionResponse.success(oChainReceipt.get()); } - ReplicateActionResponse uploadResult(String chainTaskId) { + ReplicateActionResponse uploadResult(final String chainTaskId) { final WorkerpoolAuthorization workerpoolAuthorization = contributionService.getWorkerpoolAuthorization(chainTaskId); - String resultLink = resultService.uploadResultAndGetLink(workerpoolAuthorization); - String context = "upload result"; + final String resultLink = resultService.uploadResultAndGetLink(workerpoolAuthorization); + final String context = "upload result"; if (resultLink.isEmpty()) { return getFailureResponseAndPrintErrors( - List.of(RESULT_LINK_MISSING), context, chainTaskId); + List.of(new WorkflowError(RESULT_LINK_MISSING)), context, chainTaskId); } - ComputedFile computedFile = resultService.getComputedFile(chainTaskId); - String callbackData = computedFile != null ? + final ComputedFile computedFile = resultService.getComputedFile(chainTaskId); + final String callbackData = computedFile != null ? computedFile.getCallbackData() : ""; log.info("Result uploaded [chainTaskId:{}, resultLink:{}, callbackData:{}]", @@ -410,11 +411,11 @@ ReplicateActionResponse uploadResult(String chainTaskId) { return ReplicateActionResponse.success(resultLink, callbackData); } - ReplicateActionResponse contributeAndFinalize(String chainTaskId) { + ReplicateActionResponse contributeAndFinalize(final String chainTaskId) { return contributeOrContributeAndFinalize(chainTaskId, CONTRIBUTE_AND_FINALIZE); } - ReplicateActionResponse complete(String chainTaskId) { + ReplicateActionResponse complete(final String chainTaskId) { purgeService.purgeAllServices(chainTaskId); if (!resultService.purgeTask(chainTaskId)) { @@ -472,13 +473,13 @@ boolean isValidChainReceipt(String chainTaskId, return true; } - private ReplicateActionResponse getFailureResponseAndPrintErrors(final List causes, final String context, final String chainTaskId) { - if (causes == null || causes.isEmpty()) { + private ReplicateActionResponse getFailureResponseAndPrintErrors(final List errors, final String context, final String chainTaskId) { + if (errors == null || errors.isEmpty()) { logError(UNKNOWN, context, chainTaskId); return ReplicateActionResponse.failure(); } - causes.forEach(cause -> logError(cause, context, chainTaskId)); - return ReplicateActionResponse.failure(causes.get(0)); + errors.forEach(error -> logError(error.cause(), context, chainTaskId)); + return ReplicateActionResponse.failure(errors.get(0).cause()); } /** diff --git a/src/main/java/com/iexec/worker/tee/TeeService.java b/src/main/java/com/iexec/worker/tee/TeeService.java index 4c0dfba2..c7ff4a7e 100644 --- a/src/main/java/com/iexec/worker/tee/TeeService.java +++ b/src/main/java/com/iexec/worker/tee/TeeService.java @@ -16,12 +16,12 @@ package com.iexec.worker.tee; -import com.iexec.common.replicate.ReplicateStatusCause; import com.iexec.commons.poco.task.TaskDescription; import com.iexec.sms.api.SmsClientCreationException; import com.iexec.sms.api.TeeSessionGenerationResponse; import com.iexec.worker.sgx.SgxService; import com.iexec.worker.sms.SmsService; +import com.iexec.worker.workflow.WorkflowError; import lombok.extern.slf4j.Slf4j; import java.util.Collection; @@ -47,9 +47,9 @@ public boolean isTeeEnabled() { return sgxService.isSgxEnabled(); } - public List areTeePrerequisitesMetForTask(final String chainTaskId) { + public List areTeePrerequisitesMetForTask(final String chainTaskId) { if (!isTeeEnabled()) { - return List.of(TEE_NOT_SUPPORTED); + return List.of(new WorkflowError(TEE_NOT_SUPPORTED)); } try { @@ -58,7 +58,7 @@ public List areTeePrerequisitesMetForTask(final String cha smsService.getSmsClient(chainTaskId); } catch (SmsClientCreationException e) { log.error("Couldn't get SmsClient [chainTaskId: {}]", chainTaskId, e); - return List.of(UNKNOWN_SMS); + return List.of(new WorkflowError(UNKNOWN_SMS)); } try { // Try to load the `TeeServicesProperties` relative to the task. @@ -66,10 +66,10 @@ public List areTeePrerequisitesMetForTask(final String cha teeServicesPropertiesService.getTeeServicesProperties(chainTaskId); } catch (NullPointerException e) { log.error("TEE enclave configuration is null [chainTaskId: {}]", chainTaskId, e); - return List.of(PRE_COMPUTE_MISSING_ENCLAVE_CONFIGURATION); + return List.of(new WorkflowError(PRE_COMPUTE_MISSING_ENCLAVE_CONFIGURATION)); } catch (RuntimeException e) { log.error("Couldn't get TeeServicesProperties [chainTaskId: {}]", chainTaskId, e); - return List.of(GET_TEE_SERVICES_CONFIGURATION_FAILED); + return List.of(new WorkflowError(GET_TEE_SERVICES_CONFIGURATION_FAILED)); } return List.of(); diff --git a/src/main/java/com/iexec/worker/tee/scone/TeeSconeService.java b/src/main/java/com/iexec/worker/tee/scone/TeeSconeService.java index cae973ad..bea4bdf3 100644 --- a/src/main/java/com/iexec/worker/tee/scone/TeeSconeService.java +++ b/src/main/java/com/iexec/worker/tee/scone/TeeSconeService.java @@ -16,7 +16,6 @@ package com.iexec.worker.tee.scone; -import com.iexec.common.replicate.ReplicateStatusCause; import com.iexec.commons.poco.task.TaskDescription; import com.iexec.commons.poco.tee.TeeEnclaveConfiguration; import com.iexec.sms.api.TeeSessionGenerationResponse; @@ -26,6 +25,7 @@ import com.iexec.worker.tee.TeeService; import com.iexec.worker.tee.TeeServicesPropertiesService; import com.iexec.worker.utils.LoggingUtils; +import com.iexec.worker.workflow.WorkflowError; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -64,12 +64,13 @@ public TeeSconeService( } @Override - public List areTeePrerequisitesMetForTask(final String chainTaskId) { - final List teePrerequisiteIssues = super.areTeePrerequisitesMetForTask(chainTaskId); + public List areTeePrerequisitesMetForTask(final String chainTaskId) { + final List teePrerequisiteIssues = super.areTeePrerequisitesMetForTask(chainTaskId); if (!teePrerequisiteIssues.isEmpty()) { return teePrerequisiteIssues; } - return prepareTeeForTask(chainTaskId) ? List.of() : List.of(TEE_PREPARATION_FAILED); + return prepareTeeForTask(chainTaskId) ? + List.of() : List.of(new WorkflowError(TEE_PREPARATION_FAILED)); } @Override diff --git a/src/main/java/com/iexec/worker/workflow/WorkflowError.java b/src/main/java/com/iexec/worker/workflow/WorkflowError.java new file mode 100644 index 00000000..32e0c73d --- /dev/null +++ b/src/main/java/com/iexec/worker/workflow/WorkflowError.java @@ -0,0 +1,27 @@ +/* + * Copyright 2025 IEXEC BLOCKCHAIN TECH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.iexec.worker.workflow; + +import com.iexec.common.replicate.ReplicateStatusCause; + +public record WorkflowError(ReplicateStatusCause cause, String message) { + + public WorkflowError(ReplicateStatusCause cause) { + this(cause, ""); + } + +} diff --git a/src/main/java/com/iexec/worker/utils/WorkflowException.java b/src/main/java/com/iexec/worker/workflow/WorkflowException.java similarity index 92% rename from src/main/java/com/iexec/worker/utils/WorkflowException.java rename to src/main/java/com/iexec/worker/workflow/WorkflowException.java index 0d0c78af..aabd1ce7 100644 --- a/src/main/java/com/iexec/worker/utils/WorkflowException.java +++ b/src/main/java/com/iexec/worker/workflow/WorkflowException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 IEXEC BLOCKCHAIN TECH + * Copyright 2020-2025 IEXEC BLOCKCHAIN TECH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.iexec.worker.utils; +package com.iexec.worker.workflow; import com.iexec.common.replicate.ReplicateStatusCause; import lombok.Getter; diff --git a/src/test/java/com/iexec/worker/chain/ContributionServiceTests.java b/src/test/java/com/iexec/worker/chain/ContributionServiceTests.java index bfc5aa86..86635274 100644 --- a/src/test/java/com/iexec/worker/chain/ContributionServiceTests.java +++ b/src/test/java/com/iexec/worker/chain/ContributionServiceTests.java @@ -23,6 +23,7 @@ import com.iexec.commons.poco.utils.BytesUtils; import com.iexec.commons.poco.utils.HashUtils; import com.iexec.commons.poco.utils.SignatureUtils; +import com.iexec.worker.workflow.WorkflowError; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -111,7 +112,7 @@ void getCannotContributeStatusCauseShouldReturnWorkerpoolAuthorizationNotFound() when(iexecHubService.getChainTask(chainTaskId)).thenReturn(Optional.of(chainTask)); assertThat(contributionService.getCannotContributeStatusCause(chainTaskId)) - .containsExactly(WORKERPOOL_AUTHORIZATION_NOT_FOUND); + .containsExactly(new WorkflowError(WORKERPOOL_AUTHORIZATION_NOT_FOUND)); verify(workerpoolAuthorizationService).getWorkerpoolAuthorization(chainTaskId); } @@ -125,7 +126,7 @@ void getCannotContributeStatusShouldReturnChainUnreachable() { when(iexecHubService.getChainTask(chainTaskId)).thenReturn(Optional.empty()); assertThat(contributionService.getCannotContributeStatusCause(chainTaskId)) - .containsExactly(CHAIN_UNREACHABLE); + .containsExactly(new WorkflowError(CHAIN_UNREACHABLE)); verify(iexecHubService).getChainTask(chainTaskId); } @@ -142,7 +143,7 @@ void getCannotContributeStatusShouldReturnStakeTooLow() { when(iexecHubService.getChainDeal(CHAIN_DEAL_ID)).thenReturn(Optional.of(ChainDeal.builder().workerStake(BigInteger.valueOf(5)).build())); assertThat(contributionService.getCannotContributeStatusCause(chainTaskId)) - .containsExactly(STAKE_TOO_LOW); + .containsExactly(new WorkflowError(STAKE_TOO_LOW)); verify(iexecHubService).getChainTask(chainTaskId); verify(iexecHubService).getChainAccount(); @@ -168,7 +169,7 @@ void getCannotContributeStatusShouldReturnTaskNotActive() { when(iexecHubService.getChainDeal(CHAIN_DEAL_ID)).thenReturn(Optional.of(ChainDeal.builder().workerStake(BigInteger.valueOf(5)).build())); assertThat(contributionService.getCannotContributeStatusCause(chainTaskId)) - .containsExactly(TASK_NOT_ACTIVE); + .containsExactly(new WorkflowError(TASK_NOT_ACTIVE)); verify(iexecHubService).getChainTask(chainTaskId); verify(iexecHubService).getChainAccount(); @@ -195,7 +196,7 @@ void getCannotContributeStatusShouldReturnAfterDeadline() { .thenReturn(Optional.of(ChainDeal.builder().workerStake(BigInteger.valueOf(5)).build())); assertThat(contributionService.getCannotContributeStatusCause(chainTaskId)) - .containsExactly(CONTRIBUTION_TIMEOUT); + .containsExactly(new WorkflowError(CONTRIBUTION_TIMEOUT)); verify(iexecHubService).getChainTask(chainTaskId); verify(iexecHubService).getChainAccount(); @@ -223,7 +224,7 @@ void getCannotContributeStatusShouldReturnContributionAlreadySet() { .thenReturn(Optional.of(ChainDeal.builder().workerStake(BigInteger.valueOf(5)).build())); assertThat(contributionService.getCannotContributeStatusCause(chainTaskId)) - .containsExactly(CONTRIBUTION_ALREADY_SET); + .containsExactly(new WorkflowError(CONTRIBUTION_ALREADY_SET)); verify(iexecHubService).getChainTask(chainTaskId); verify(iexecHubService).getChainAccount(); @@ -293,7 +294,11 @@ void getCannotContributeStatusShouldReturnMultipleErrors() { .thenReturn(Optional.of(ChainDeal.builder().workerStake(BigInteger.valueOf(5)).build())); assertThat(contributionService.getCannotContributeStatusCause(chainTaskId)) - .containsExactly(STAKE_TOO_LOW, TASK_NOT_ACTIVE, CONTRIBUTION_TIMEOUT, CONTRIBUTION_ALREADY_SET); + .containsExactly( + new WorkflowError(STAKE_TOO_LOW), + new WorkflowError(TASK_NOT_ACTIVE), + new WorkflowError(CONTRIBUTION_TIMEOUT), + new WorkflowError(CONTRIBUTION_ALREADY_SET)); verify(iexecHubService).getChainTask(chainTaskId); verify(iexecHubService).getChainAccount(); @@ -309,7 +314,9 @@ void getCannotContributeStatusShouldReturnAuthAndChainUnreachableErrors() { when(iexecHubService.getChainTask(chainTaskId)).thenReturn(Optional.empty()); assertThat(contributionService.getCannotContributeStatusCause(chainTaskId)) - .containsExactly(WORKERPOOL_AUTHORIZATION_NOT_FOUND, CHAIN_UNREACHABLE); + .containsExactly( + new WorkflowError(WORKERPOOL_AUTHORIZATION_NOT_FOUND), + new WorkflowError(CHAIN_UNREACHABLE)); verify(workerpoolAuthorizationService).getWorkerpoolAuthorization(chainTaskId); verify(iexecHubService).getChainTask(chainTaskId); @@ -328,7 +335,7 @@ void getCannotContributeAndFinalizeStatusCauseShouldReturnTrustNotOne() { when(iexecHubService.getChainTask(chainTaskId)).thenReturn(Optional.of(chainTask)); assertThat(contributionService.getCannotContributeAndFinalizeStatusCause(chainTaskId)) - .containsExactly(TRUST_NOT_1); + .containsExactly(new WorkflowError(TRUST_NOT_1)); } @Test @@ -339,7 +346,7 @@ void getCannotContributeAndFinalizeStatusCauseShouldReturnChainUnreachable() { when(iexecHubService.getChainTask(chainTaskId)).thenReturn(Optional.empty()); assertThat(contributionService.getCannotContributeAndFinalizeStatusCause(chainTaskId)) - .containsExactly(CHAIN_UNREACHABLE); + .containsExactly(new WorkflowError(CHAIN_UNREACHABLE)); verify(iexecHubService).getChainTask(chainTaskId); } @@ -359,7 +366,7 @@ void getCannotContributeAndFinalizeStatusCauseShouldReturnTaskAlreadyContributed when(iexecHubService.getChainTask(chainTaskId)).thenReturn(Optional.of(chainTaskWithContribution)); assertThat(contributionService.getCannotContributeAndFinalizeStatusCause(chainTaskId)) - .containsExactly(TASK_ALREADY_CONTRIBUTED); + .containsExactly(new WorkflowError(TASK_ALREADY_CONTRIBUTED)); } @Test @@ -383,7 +390,9 @@ void getCannotContributeAndFinalizeStatusCauseShouldReturnMultipleErrors() { when(iexecHubService.getChainTask(chainTaskId)).thenReturn(Optional.empty()); assertThat(contributionService.getCannotContributeAndFinalizeStatusCause(chainTaskId)) - .containsExactly(TRUST_NOT_1, CHAIN_UNREACHABLE); + .containsExactly( + new WorkflowError(TRUST_NOT_1), + new WorkflowError(CHAIN_UNREACHABLE)); verify(iexecHubService).getChainTask(chainTaskId); } @@ -406,7 +415,9 @@ void getCannotContributeAndFinalizeStatusCauseShouldReturnTrustAndTaskAlreadyCon when(iexecHubService.getChainTask(chainTaskId)).thenReturn(Optional.of(chainTaskWithContribution)); assertThat(contributionService.getCannotContributeAndFinalizeStatusCause(chainTaskId)) - .containsExactly(TRUST_NOT_1, TASK_ALREADY_CONTRIBUTED); + .containsExactly( + new WorkflowError(TRUST_NOT_1), + new WorkflowError(TASK_ALREADY_CONTRIBUTED)); verify(iexecHubService).getChainTask(chainTaskId); } diff --git a/src/test/java/com/iexec/worker/compute/ComputeControllerTests.java b/src/test/java/com/iexec/worker/compute/ComputeControllerTests.java index 6a383073..9e0b8076 100644 --- a/src/test/java/com/iexec/worker/compute/ComputeControllerTests.java +++ b/src/test/java/com/iexec/worker/compute/ComputeControllerTests.java @@ -20,6 +20,7 @@ import com.iexec.common.result.ComputedFile; import com.iexec.worker.chain.WorkerpoolAuthorizationService; import com.iexec.worker.result.ResultService; +import com.iexec.worker.workflow.WorkflowError; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -40,14 +41,16 @@ import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) -public class ComputeControllerTests { +class ComputeControllerTests { - public static final String CHAIN_TASK_ID = "0xtask"; - public static final ReplicateStatusCause CAUSE = ReplicateStatusCause.PRE_COMPUTE_INPUT_FILE_DOWNLOAD_FAILED; + private static final String CHAIN_TASK_ID = "0xtask"; + private static final WorkflowError ERROR = new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_INPUT_FILE_DOWNLOAD_FAILED); + private static final WorkflowError UNKNOWN_PRE_ERROR = new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_FAILED_UNKNOWN_ISSUE); + private static final WorkflowError UNKNOWN_POST_ERROR = new WorkflowError(ReplicateStatusCause.POST_COMPUTE_FAILED_UNKNOWN_ISSUE); private static final String AUTH_HEADER = "Bearer validToken"; - private static final List MULTIPLE_CAUSES = List.of( - ReplicateStatusCause.PRE_COMPUTE_DATASET_URL_MISSING, - ReplicateStatusCause.PRE_COMPUTE_INVALID_DATASET_CHECKSUM + private static final List MULTIPLE_ERRORS = List.of( + new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_DATASET_URL_MISSING), + new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_INVALID_DATASET_CHECKSUM) ); private final ComputedFile computedFile = new ComputedFile( "/path", @@ -75,71 +78,71 @@ void setUp() { } // region sendExitCauseForGivenComputeStage - private ResponseEntity getResponse(final ComputeStage stage, final List causes) { + private ResponseEntity getResponse(final ComputeStage stage, final List errors) { return computeController.sendExitCausesForGivenComputeStage( AUTH_HEADER, stage, CHAIN_TASK_ID, - causes + errors ); } static Stream simpleAndListExitCauses() { return Stream.of( - Arguments.of(ComputeStage.PRE, List.of(CAUSE), ReplicateStatusCause.PRE_COMPUTE_FAILED_UNKNOWN_ISSUE), - Arguments.of(ComputeStage.POST, List.of(CAUSE), ReplicateStatusCause.POST_COMPUTE_FAILED_UNKNOWN_ISSUE), - Arguments.of(ComputeStage.PRE, MULTIPLE_CAUSES, ReplicateStatusCause.PRE_COMPUTE_FAILED_UNKNOWN_ISSUE), - Arguments.of(ComputeStage.POST, MULTIPLE_CAUSES, ReplicateStatusCause.POST_COMPUTE_FAILED_UNKNOWN_ISSUE) + Arguments.of(ComputeStage.PRE, List.of(ERROR), UNKNOWN_PRE_ERROR), + Arguments.of(ComputeStage.POST, List.of(ERROR), UNKNOWN_POST_ERROR), + Arguments.of(ComputeStage.PRE, MULTIPLE_ERRORS, UNKNOWN_PRE_ERROR), + Arguments.of(ComputeStage.POST, MULTIPLE_ERRORS, UNKNOWN_POST_ERROR) ); } @ParameterizedTest @MethodSource("simpleAndListExitCauses") - void shouldReturnOkWhenSendingExitCause(final ComputeStage stage, final List causes) { + void shouldReturnOkWhenSendingExitCause(final ComputeStage stage, final List errors) { when(workerpoolAuthorizationService.isSignedWithEnclaveChallenge(CHAIN_TASK_ID, AUTH_HEADER)) .thenReturn(true); - final ResponseEntity response = getResponse(stage, causes); + final ResponseEntity response = getResponse(stage, errors); assertThat(response.getStatusCode().is2xxSuccessful()).isTrue(); assertThat(HttpStatus.OK.value()).isEqualTo(response.getStatusCode().value()); } @ParameterizedTest @MethodSource("simpleAndListExitCauses") - void shouldReturnAlreadyReportedWhenCalledMultipleTimes(final ComputeStage stage, final List causes, ReplicateStatusCause fallbackCause) { + void shouldReturnAlreadyReportedWhenCalledMultipleTimes(final ComputeStage stage, final List errors, WorkflowError fallbackError) { when(workerpoolAuthorizationService.isSignedWithEnclaveChallenge(CHAIN_TASK_ID, AUTH_HEADER)) .thenReturn(true); - final ResponseEntity firstResponse = getResponse(stage, causes); + final ResponseEntity firstResponse = getResponse(stage, errors); assertThat(firstResponse.getStatusCode().value()).isEqualTo(HttpStatus.OK.value()); - final ResponseEntity secondResponse = getResponse(stage, causes); + final ResponseEntity secondResponse = getResponse(stage, errors); assertThat(secondResponse.getStatusCode().value()).isEqualTo(HttpStatus.ALREADY_REPORTED.value()); - final List retrievedCauses = computeStageExitService - .getExitCausesAndPruneForGivenComputeStage(CHAIN_TASK_ID, stage, fallbackCause); + final List retrievedCauses = computeStageExitService + .getExitCausesAndPruneForGivenComputeStage(CHAIN_TASK_ID, stage, fallbackError); assertThat(retrievedCauses) - .hasSize(causes.size()) - .containsAll(causes); + .hasSize(errors.size()) + .containsAll(errors); } @ParameterizedTest @MethodSource("simpleAndListExitCauses") - void shouldReturnUnauthorizedWhenAuthFails(final ComputeStage stage, final List causes) { + void shouldReturnUnauthorizedWhenAuthFails(final ComputeStage stage, final List errors) { when(workerpoolAuthorizationService.isSignedWithEnclaveChallenge(CHAIN_TASK_ID, AUTH_HEADER)) .thenReturn(false); - final ResponseEntity response = getResponse(stage, causes); + final ResponseEntity response = getResponse(stage, errors); assertThat(HttpStatus.UNAUTHORIZED.value()).isEqualTo(response.getStatusCode().value()); } @ParameterizedTest @MethodSource("simpleAndListExitCauses") - void shouldReturnNotFoundWhenWrongChainTaskId(final ComputeStage stage, final List causes) { + void shouldReturnNotFoundWhenWrongChainTaskId(final ComputeStage stage, final List errors) { when(workerpoolAuthorizationService.isSignedWithEnclaveChallenge(CHAIN_TASK_ID, AUTH_HEADER)) .thenThrow(NoSuchElementException.class); - final ResponseEntity response = getResponse(stage, causes); + final ResponseEntity response = getResponse(stage, errors); assertThat(HttpStatus.NOT_FOUND.value()).isEqualTo(response.getStatusCode().value()); } @@ -154,11 +157,11 @@ static Stream badRequestScenariosArguments() { @ParameterizedTest @MethodSource("badRequestScenariosArguments") - void shouldReturnBadRequestForInvalidInputs(final ComputeStage stage, final List causes) { + void shouldReturnBadRequestForInvalidInputs(final ComputeStage stage, final List errors) { when(workerpoolAuthorizationService.isSignedWithEnclaveChallenge(CHAIN_TASK_ID, AUTH_HEADER)) .thenReturn(true); - final ResponseEntity response = getResponse(stage, causes); + final ResponseEntity response = getResponse(stage, errors); assertThat(HttpStatus.BAD_REQUEST.value()).isEqualTo(response.getStatusCode().value()); } // endregion diff --git a/src/test/java/com/iexec/worker/compute/ComputeExitCauseServiceTests.java b/src/test/java/com/iexec/worker/compute/ComputeExitCauseServiceTests.java index 1199e4d1..10df1f06 100644 --- a/src/test/java/com/iexec/worker/compute/ComputeExitCauseServiceTests.java +++ b/src/test/java/com/iexec/worker/compute/ComputeExitCauseServiceTests.java @@ -17,6 +17,7 @@ package com.iexec.worker.compute; import com.iexec.common.replicate.ReplicateStatusCause; +import com.iexec.worker.workflow.WorkflowError; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -30,13 +31,17 @@ class ComputeExitCauseServiceTests { - public static final String CHAIN_TASK_ID = "chainTaskId"; - private static final ReplicateStatusCause DEFAULT_PRE_CAUSE = ReplicateStatusCause.PRE_COMPUTE_FAILED_UNKNOWN_ISSUE; - private static final ReplicateStatusCause DEFAULT_POST_CAUSE = ReplicateStatusCause.POST_COMPUTE_FAILED_UNKNOWN_ISSUE; - private static final List SINGLE_PRE_CAUSES = List.of(ReplicateStatusCause.PRE_COMPUTE_DATASET_URL_MISSING); - private static final List MULTIPLE_PRE_CAUSES = List.of(ReplicateStatusCause.PRE_COMPUTE_DATASET_URL_MISSING, ReplicateStatusCause.PRE_COMPUTE_INVALID_DATASET_CHECKSUM); - private static final List SINGLE_POST_CAUSES = List.of(ReplicateStatusCause.POST_COMPUTE_COMPUTED_FILE_NOT_FOUND); - private static final List MULTIPLE_POST_CAUSES = List.of(ReplicateStatusCause.POST_COMPUTE_COMPUTED_FILE_NOT_FOUND, ReplicateStatusCause.POST_COMPUTE_TIMEOUT); + private static final String CHAIN_TASK_ID = "chainTaskId"; + private static final WorkflowError DEFAULT_PRE_ERROR = new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_FAILED_UNKNOWN_ISSUE); + private static final WorkflowError DEFAULT_POST_ERROR = new WorkflowError(ReplicateStatusCause.POST_COMPUTE_FAILED_UNKNOWN_ISSUE); + private static final List SINGLE_PRE_ERRORS = List.of(new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_DATASET_URL_MISSING)); + private static final List MULTIPLE_PRE_ERRORS = List.of( + new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_DATASET_URL_MISSING), + new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_INVALID_DATASET_CHECKSUM)); + private static final List SINGLE_POST_ERRORS = List.of(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_COMPUTED_FILE_NOT_FOUND)); + private static final List MULTIPLE_POST_ERRORS = List.of( + new WorkflowError(ReplicateStatusCause.POST_COMPUTE_COMPUTED_FILE_NOT_FOUND), + new WorkflowError(ReplicateStatusCause.POST_COMPUTE_TIMEOUT)); private ComputeExitCauseService computeExitCauseService; @@ -48,108 +53,70 @@ void before() { //region setAndGetExitCausesParameterizedTests static Stream computeStageAndCausesArguments() { return Stream.of( - Arguments.of( - ComputeStage.PRE, - SINGLE_PRE_CAUSES, - DEFAULT_PRE_CAUSE - ), - Arguments.of( - ComputeStage.PRE, - List.of( - ReplicateStatusCause.PRE_COMPUTE_DATASET_URL_MISSING, - ReplicateStatusCause.PRE_COMPUTE_INVALID_DATASET_CHECKSUM - ), - DEFAULT_PRE_CAUSE - ), - Arguments.of( - ComputeStage.POST, - SINGLE_POST_CAUSES, - DEFAULT_POST_CAUSE - ), - Arguments.of( - ComputeStage.POST, - List.of( - ReplicateStatusCause.POST_COMPUTE_COMPUTED_FILE_NOT_FOUND, - ReplicateStatusCause.POST_COMPUTE_TIMEOUT - ), - DEFAULT_POST_CAUSE - ) + Arguments.of(ComputeStage.PRE, SINGLE_PRE_ERRORS, DEFAULT_PRE_ERROR), + Arguments.of(ComputeStage.PRE, MULTIPLE_PRE_ERRORS, DEFAULT_PRE_ERROR), + Arguments.of(ComputeStage.POST, SINGLE_POST_ERRORS, DEFAULT_POST_ERROR), + Arguments.of(ComputeStage.POST, MULTIPLE_POST_ERRORS, DEFAULT_POST_ERROR) ); } @ParameterizedTest @MethodSource("computeStageAndCausesArguments") - void shouldSetExitCausesSuccessfully(final ComputeStage stage, final List causes) { - assertThat(computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, causes)).isTrue(); + void shouldSetExitCausesSuccessfully(final ComputeStage stage, final List errors) { + assertThat(computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, errors)).isTrue(); } @ParameterizedTest @MethodSource("computeStageAndCausesArguments") - void shouldGetExitCausesAfterSetting(final ComputeStage stage, final List causes, final ReplicateStatusCause defaultCause) { - computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, causes); - assertThat(computeExitCauseService.getExitCausesAndPruneForGivenComputeStage(CHAIN_TASK_ID, stage, defaultCause)).containsExactlyElementsOf(causes); + void shouldGetExitCausesAfterSetting(final ComputeStage stage, final List errors, final WorkflowError defaultError) { + computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, errors); + assertThat(computeExitCauseService.getExitCausesAndPruneForGivenComputeStage(CHAIN_TASK_ID, stage, defaultError)).containsExactlyElementsOf(errors); } @ParameterizedTest @MethodSource("computeStageAndCausesArguments") - void shouldReturnDefaultCauseAfterPruning(final ComputeStage stage, final List causes, final ReplicateStatusCause defaultCause) { - computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, causes); - computeExitCauseService.getExitCausesAndPruneForGivenComputeStage(CHAIN_TASK_ID, stage, defaultCause); - assertThat(computeExitCauseService.getExitCausesAndPruneForGivenComputeStage(CHAIN_TASK_ID, stage, defaultCause)).isEqualTo(List.of(defaultCause)); + void shouldReturnDefaultCauseAfterPruning(final ComputeStage stage, final List errors, final WorkflowError defaultError) { + computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, errors); + computeExitCauseService.getExitCausesAndPruneForGivenComputeStage(CHAIN_TASK_ID, stage, defaultError); + assertThat(computeExitCauseService.getExitCausesAndPruneForGivenComputeStage(CHAIN_TASK_ID, stage, defaultError)).containsExactly(defaultError); } //endregion //region Report Once Behavior - static Stream validExitCauseProvider() { - return Stream.of( - Arguments.of(ComputeStage.PRE, SINGLE_PRE_CAUSES, DEFAULT_PRE_CAUSE), - Arguments.of(ComputeStage.PRE, MULTIPLE_PRE_CAUSES, DEFAULT_PRE_CAUSE), - Arguments.of(ComputeStage.POST, SINGLE_POST_CAUSES, DEFAULT_POST_CAUSE), - Arguments.of(ComputeStage.POST, MULTIPLE_POST_CAUSES, DEFAULT_POST_CAUSE) - ); - } - - @ParameterizedTest - @MethodSource("validExitCauseProvider") - void shouldReturnTrueWhenReportingForFirstTime(final ComputeStage stage, final List causes) { - assertThat(computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, causes)) - .isTrue(); - } - @ParameterizedTest - @MethodSource("validExitCauseProvider") - void shouldReturnFalseWhenReportingTwiceWithSameCauses(final ComputeStage stage, final List causes) { - computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, causes); - assertThat(computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, causes)).isFalse(); + @MethodSource("computeStageAndCausesArguments") + void shouldReturnFalseWhenReportingTwiceWithSameCauses(final ComputeStage stage, final List errors) { + computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, errors); + assertThat(computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, errors)).isFalse(); } @ParameterizedTest - @MethodSource("validExitCauseProvider") - void shouldReturnFalseWhenReportingTwiceWithDifferentCauses(final ComputeStage stage, final List causes) { - computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, causes); - List differentCauses = stage == ComputeStage.PRE - ? List.of(ReplicateStatusCause.PRE_COMPUTE_INVALID_DATASET_CHECKSUM) - : List.of(ReplicateStatusCause.POST_COMPUTE_TIMEOUT); + @MethodSource("computeStageAndCausesArguments") + void shouldReturnFalseWhenReportingTwiceWithDifferentCauses(final ComputeStage stage, final List errors) { + computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, errors); + final List differentCauses = stage == ComputeStage.PRE + ? List.of(new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_INVALID_DATASET_CHECKSUM)) + : List.of(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_TIMEOUT)); assertThat(computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, differentCauses)).isFalse(); } @ParameterizedTest - @MethodSource("validExitCauseProvider") - void shouldReturnOriginalCausesAfterSuccessfulReport(final ComputeStage stage, final List causes, final ReplicateStatusCause defaultCause) { - computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, causes); - assertThat(computeExitCauseService.getExitCausesAndPruneForGivenComputeStage(CHAIN_TASK_ID, stage, defaultCause)).isEqualTo(causes); + @MethodSource("computeStageAndCausesArguments") + void shouldReturnOriginalCausesAfterSuccessfulReport(final ComputeStage stage, final List errors, final WorkflowError defaultError) { + computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, stage, errors); + assertThat(computeExitCauseService.getExitCausesAndPruneForGivenComputeStage(CHAIN_TASK_ID, stage, defaultError)).isEqualTo(errors); } @Test void shouldAllowReportingPostStageAfterPreStageForSameTask() { - computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, ComputeStage.PRE, SINGLE_PRE_CAUSES); - assertThat(computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, ComputeStage.POST, SINGLE_POST_CAUSES)).isTrue(); + computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, ComputeStage.PRE, SINGLE_PRE_ERRORS); + assertThat(computeExitCauseService.setExitCausesForGivenComputeStage(CHAIN_TASK_ID, ComputeStage.POST, SINGLE_POST_ERRORS)).isTrue(); } @Test void shouldReturnDefaultCauseWhenNoCausesWereSet() { - assertThat(computeExitCauseService.getExitCausesAndPruneForGivenComputeStage(CHAIN_TASK_ID, ComputeStage.PRE, DEFAULT_PRE_CAUSE)) - .isEqualTo(List.of(DEFAULT_PRE_CAUSE)); + assertThat(computeExitCauseService.getExitCausesAndPruneForGivenComputeStage(CHAIN_TASK_ID, ComputeStage.PRE, DEFAULT_PRE_ERROR)) + .containsExactly(DEFAULT_PRE_ERROR); } //endregion } diff --git a/src/test/java/com/iexec/worker/compute/ComputeManagerServiceTests.java b/src/test/java/com/iexec/worker/compute/ComputeManagerServiceTests.java index 3192f6e9..c4db0c44 100644 --- a/src/test/java/com/iexec/worker/compute/ComputeManagerServiceTests.java +++ b/src/test/java/com/iexec/worker/compute/ComputeManagerServiceTests.java @@ -35,6 +35,7 @@ import com.iexec.worker.docker.DockerRegistryConfiguration; import com.iexec.worker.docker.DockerService; import com.iexec.worker.result.ResultService; +import com.iexec.worker.workflow.WorkflowError; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.io.TempDir; @@ -202,7 +203,7 @@ void shouldRunTeePreComputeWithFailureResponse() { when(preComputeService.runTeePreCompute(taskDescription, workerpoolAuthorization)).thenReturn(PreComputeResponse.builder() .secureSession(null) - .exitCauses(List.of(ReplicateStatusCause.PRE_COMPUTE_DATASET_URL_MISSING)) + .exitCauses(List.of(new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_DATASET_URL_MISSING))) .build()); PreComputeResponse preComputeResponse = @@ -211,7 +212,7 @@ void shouldRunTeePreComputeWithFailureResponse() { assertThat(preComputeResponse.getSecureSession()).isNull(); assertThat(preComputeResponse.isSuccessful()).isFalse(); assertThat(preComputeResponse.getExitCauses()) - .containsExactly(ReplicateStatusCause.PRE_COMPUTE_DATASET_URL_MISSING); + .containsExactly(new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_DATASET_URL_MISSING)); } //endregion @@ -245,7 +246,7 @@ void shouldRunStandardComputeWithFailureResponse() { final TaskDescription taskDescription = createTaskDescriptionBuilder(false).build(); AppComputeResponse expectedDockerRunResponse = AppComputeResponse.builder() - .exitCauses(List.of(ReplicateStatusCause.APP_COMPUTE_FAILED)) + .exitCauses(List.of(new WorkflowError(ReplicateStatusCause.APP_COMPUTE_FAILED))) .stdout(dockerLogs.getStdout()) .stderr(dockerLogs.getStderr()) .build(); @@ -292,7 +293,7 @@ void shouldRunTeeComputeWithFailure() { final TaskDescription taskDescription = createTaskDescriptionBuilder(true).build(); AppComputeResponse expectedDockerRunResponse = AppComputeResponse.builder() - .exitCauses(List.of(ReplicateStatusCause.APP_COMPUTE_FAILED)) + .exitCauses(List.of(new WorkflowError(ReplicateStatusCause.APP_COMPUTE_FAILED))) .stdout(dockerLogs.getStdout()) .stderr(dockerLogs.getStderr()) .build(); @@ -319,7 +320,8 @@ void shouldNotBeSuccessfulWhenComputedFileNotFound() { when(resultService.readComputedFile(CHAIN_TASK_ID)).thenReturn(null); PostComputeResponse postComputeResponse = computeManagerService.runPostCompute(taskDescription, null); assertThat(postComputeResponse.isSuccessful()).isFalse(); - assertThat(postComputeResponse.getExitCauses()).containsExactly(ReplicateStatusCause.POST_COMPUTE_COMPUTED_FILE_NOT_FOUND); + assertThat(postComputeResponse.getExitCauses()) + .containsExactly(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_COMPUTED_FILE_NOT_FOUND)); } @Test @@ -332,7 +334,8 @@ void shouldNotBeSuccessfulWhenResultDigestComputationFails() { when(resultService.computeResultDigest(computedFile)).thenReturn(""); PostComputeResponse postComputeResponse = computeManagerService.runPostCompute(taskDescription, null); assertThat(postComputeResponse.isSuccessful()).isFalse(); - assertThat(postComputeResponse.getExitCauses()).containsExactly(ReplicateStatusCause.POST_COMPUTE_RESULT_DIGEST_COMPUTATION_FAILED); + assertThat(postComputeResponse.getExitCauses()) + .containsExactly(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_RESULT_DIGEST_COMPUTATION_FAILED)); } @Test @@ -357,12 +360,15 @@ void shouldRunStandardPostCompute() { @EnumSource(value = ReplicateStatusCause.class, names = "POST_COMPUTE_.*", mode = EnumSource.Mode.MATCH_ALL) void shouldRunStandardPostComputeWithFailureResponse(ReplicateStatusCause statusCause) { final TaskDescription taskDescription = createTaskDescriptionBuilder(false).build(); - PostComputeResponse postComputeResponse = PostComputeResponse.builder().exitCauses(List.of(statusCause)).build(); + PostComputeResponse postComputeResponse = PostComputeResponse.builder() + .exitCauses(List.of(new WorkflowError(statusCause))) + .build(); when(postComputeService.runStandardPostCompute(taskDescription)).thenReturn(postComputeResponse); postComputeResponse = computeManagerService.runPostCompute(taskDescription, null); assertThat(postComputeResponse.isSuccessful()).isFalse(); - assertThat(postComputeResponse.getExitCauses()).containsExactly(statusCause); + assertThat(postComputeResponse.getExitCauses()) + .containsExactly(new WorkflowError(statusCause)); } @Test @@ -399,7 +405,7 @@ void shouldRunTeePostComputeWithFailureResponse() { final TaskDescription taskDescription = createTaskDescriptionBuilder(true).build(); PostComputeResponse expectedDockerRunResponse = PostComputeResponse.builder() - .exitCauses(List.of(ReplicateStatusCause.APP_COMPUTE_FAILED)) + .exitCauses(List.of(new WorkflowError(ReplicateStatusCause.APP_COMPUTE_FAILED))) .stdout(dockerLogs.getStdout()) .stderr(dockerLogs.getStderr()) .build(); diff --git a/src/test/java/com/iexec/worker/compute/post/PostComputeServiceTests.java b/src/test/java/com/iexec/worker/compute/post/PostComputeServiceTests.java index 9268cbbe..ffa373ab 100644 --- a/src/test/java/com/iexec/worker/compute/post/PostComputeServiceTests.java +++ b/src/test/java/com/iexec/worker/compute/post/PostComputeServiceTests.java @@ -40,6 +40,7 @@ import com.iexec.worker.tee.TeeService; import com.iexec.worker.tee.TeeServicesManager; import com.iexec.worker.tee.TeeServicesPropertiesService; +import com.iexec.worker.workflow.WorkflowError; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -283,13 +284,14 @@ void shouldNotRunTeePostComputeSinceDockerImageNotFoundLocally() { PostComputeResponse postComputeResponse = postComputeService.runTeePostCompute(taskDescription, SECURE_SESSION); assertThat(postComputeResponse.isSuccessful()).isFalse(); - assertThat(postComputeResponse.getExitCauses()).containsExactly(ReplicateStatusCause.POST_COMPUTE_IMAGE_MISSING); + assertThat(postComputeResponse.getExitCauses()) + .containsExactly(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_IMAGE_MISSING)); verify(dockerService, never()).run(any()); } @ParameterizedTest @MethodSource("shouldRunTeePostComputeWithFailDockerResponseArgs") - void shouldRunTeePostComputeWithFailDockerResponse(Map.Entry exitCodeKeyToExpectedCauseValue) { + void shouldRunTeePostComputeWithFailDockerResponse(Map.Entry exitCodeKeyToExpectedCauseValue) { taskDescription = TaskDescription.builder() .chainTaskId(CHAIN_TASK_ID) .datasetUri(DATASET_URI) @@ -303,7 +305,7 @@ void shouldRunTeePostComputeWithFailDockerResponse(Map.Entry> shouldRunTeePostComputeWithFailDockerResponseArgs() { + private static Stream> shouldRunTeePostComputeWithFailDockerResponseArgs() { return Map.of( - 1, ReplicateStatusCause.POST_COMPUTE_COMPUTED_FILE_NOT_FOUND, - 2, ReplicateStatusCause.POST_COMPUTE_EXIT_REPORTING_FAILED, - 3, ReplicateStatusCause.POST_COMPUTE_TASK_ID_MISSING + 1, new WorkflowError(ReplicateStatusCause.POST_COMPUTE_COMPUTED_FILE_NOT_FOUND), + 2, new WorkflowError(ReplicateStatusCause.POST_COMPUTE_EXIT_REPORTING_FAILED), + 3, new WorkflowError(ReplicateStatusCause.POST_COMPUTE_TASK_ID_MISSING) ).entrySet().stream(); } @@ -342,7 +344,7 @@ void shouldNotRunTeePostComputeSinceTimeout() { assertThat(postComputeResponse.isSuccessful()).isFalse(); assertThat(postComputeResponse.getExitCauses()) - .containsExactly(ReplicateStatusCause.POST_COMPUTE_TIMEOUT); + .containsExactly(new WorkflowError(ReplicateStatusCause.POST_COMPUTE_TIMEOUT)); verify(dockerService).run(any()); } @@ -363,8 +365,7 @@ void shouldReturnUnknownIssueForUnmappedExitCodes(int exitCode) { final PostComputeResponse response = postComputeService.runTeePostCompute(taskDescription, SECURE_SESSION); assertThat(response.isSuccessful()).isFalse(); assertThat(response.getExitCauses()) - .hasSize(1) - .containsExactly(POST_COMPUTE_FAILED_UNKNOWN_ISSUE); + .containsExactly(new WorkflowError(POST_COMPUTE_FAILED_UNKNOWN_ISSUE)); } //endregion } diff --git a/src/test/java/com/iexec/worker/compute/pre/PreComputeServiceTests.java b/src/test/java/com/iexec/worker/compute/pre/PreComputeServiceTests.java index 108b9298..340ad383 100644 --- a/src/test/java/com/iexec/worker/compute/pre/PreComputeServiceTests.java +++ b/src/test/java/com/iexec/worker/compute/pre/PreComputeServiceTests.java @@ -43,6 +43,7 @@ import com.iexec.worker.tee.TeeService; import com.iexec.worker.tee.TeeServicesManager; import com.iexec.worker.tee.TeeServicesPropertiesService; +import com.iexec.worker.workflow.WorkflowError; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; @@ -240,7 +241,8 @@ void shouldFailToRunTeePreComputeSinceMissingEnclaveConfiguration() { final PreComputeResponse response = preComputeService.runTeePreCompute(taskDescription, workerpoolAuthorization); assertThat(response.isSuccessful()).isFalse(); - assertThat(response.getExitCauses()).containsExactly(PRE_COMPUTE_MISSING_ENCLAVE_CONFIGURATION); + assertThat(response.getExitCauses()) + .containsExactly(new WorkflowError(PRE_COMPUTE_MISSING_ENCLAVE_CONFIGURATION)); verifyNoInteractions(smsService); } @@ -252,7 +254,8 @@ void shouldFailToRunTeePreComputeSinceInvalidEnclaveConfiguration() { final PreComputeResponse response = preComputeService.runTeePreCompute(taskDescription, workerpoolAuthorization); assertThat(response.isSuccessful()).isFalse(); - assertThat(response.getExitCauses()).containsExactly(PRE_COMPUTE_INVALID_ENCLAVE_CONFIGURATION); + assertThat(response.getExitCauses()) + .containsExactly(new WorkflowError(PRE_COMPUTE_INVALID_ENCLAVE_CONFIGURATION)); verifyNoInteractions(smsService); } @@ -297,13 +300,14 @@ void shouldNotRunTeePreComputeSinceDockerImageNotFoundLocally() throws TeeSessio final PreComputeResponse preComputeResponse = preComputeService.runTeePreCompute(taskDescription, workerpoolAuthorization); assertThat(preComputeResponse.isSuccessful()).isFalse(); - assertThat(preComputeResponse.getExitCauses()).containsExactly(ReplicateStatusCause.PRE_COMPUTE_IMAGE_MISSING); + assertThat(preComputeResponse.getExitCauses()) + .containsExactly(new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_IMAGE_MISSING)); verify(dockerService, never()).run(any()); } @ParameterizedTest @MethodSource("shouldFailToRunTeePreComputeSinceDockerRunFailedArgs") - void shouldFailToRunTeePreComputeSinceDockerRunFailed(Map.Entry exitCodeKeyToExpectedCauseValue) throws TeeSessionGenerationException { + void shouldFailToRunTeePreComputeSinceDockerRunFailed(Map.Entry exitCodeKeyToExpectedCauseValue) throws TeeSessionGenerationException { final TaskDescription taskDescription = taskDescriptionBuilder.build(); DockerRunResponse dockerRunResponse = DockerRunResponse.builder() .containerExitCode(exitCodeKeyToExpectedCauseValue.getKey()) @@ -312,7 +316,7 @@ void shouldFailToRunTeePreComputeSinceDockerRunFailed(Map.Entry> shouldFailToRunTeePreComputeSinceDockerRunFailedArgs() { + private static Stream> shouldFailToRunTeePreComputeSinceDockerRunFailedArgs() { return Map.of( - 1, ReplicateStatusCause.PRE_COMPUTE_DATASET_URL_MISSING, - 2, ReplicateStatusCause.PRE_COMPUTE_EXIT_REPORTING_FAILED, - 3, ReplicateStatusCause.PRE_COMPUTE_TASK_ID_MISSING + 1, new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_DATASET_URL_MISSING), + 2, new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_EXIT_REPORTING_FAILED), + 3, new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_TASK_ID_MISSING) ).entrySet().stream(); } @@ -347,7 +351,7 @@ void shouldFailToRunTeePreComputeSinceTimeout() throws TeeSessionGenerationExcep assertThat(preComputeResponse.isSuccessful()).isFalse(); assertThat(preComputeResponse.getExitCauses()) - .containsExactly(ReplicateStatusCause.PRE_COMPUTE_TIMEOUT); + .containsExactly(new WorkflowError(ReplicateStatusCause.PRE_COMPUTE_TIMEOUT)); verify(dockerService).run(any()); } @@ -441,8 +445,7 @@ void shouldReturnUnknownIssueForUnmappedExitCodes(int exitCode) throws TeeSessio final PreComputeResponse response = preComputeService.runTeePreCompute(taskDescription, workerpoolAuthorization); assertThat(response.isSuccessful()).isFalse(); assertThat(response.getExitCauses()) - .hasSize(1) - .containsExactly(PRE_COMPUTE_FAILED_UNKNOWN_ISSUE); + .containsExactly(new WorkflowError(PRE_COMPUTE_FAILED_UNKNOWN_ISSUE)); } // endregion } diff --git a/src/test/java/com/iexec/worker/dataset/DataServiceTests.java b/src/test/java/com/iexec/worker/dataset/DataServiceTests.java index 6d524313..48eac955 100644 --- a/src/test/java/com/iexec/worker/dataset/DataServiceTests.java +++ b/src/test/java/com/iexec/worker/dataset/DataServiceTests.java @@ -20,7 +20,7 @@ import com.iexec.common.utils.FileHashUtils; import com.iexec.commons.poco.task.TaskDescription; import com.iexec.worker.config.WorkerConfigurationService; -import com.iexec.worker.utils.WorkflowException; +import com.iexec.worker.workflow.WorkflowException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/src/test/java/com/iexec/worker/result/ResultServiceTests.java b/src/test/java/com/iexec/worker/result/ResultServiceTests.java index 88fbc4ab..67b0b29f 100644 --- a/src/test/java/com/iexec/worker/result/ResultServiceTests.java +++ b/src/test/java/com/iexec/worker/result/ResultServiceTests.java @@ -32,6 +32,7 @@ import com.iexec.worker.chain.IexecHubService; import com.iexec.worker.config.PublicConfigurationService; import com.iexec.worker.config.WorkerConfigurationService; +import com.iexec.worker.workflow.WorkflowError; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -117,7 +118,7 @@ void shouldWriteErrorToIexecOut() { boolean isErrorWritten = resultService.writeErrorToIexecOut(CHAIN_TASK_ID, ReplicateStatus.DATA_DOWNLOAD_FAILED, - List.of(ReplicateStatusCause.INPUT_FILES_DOWNLOAD_FAILED)); + List.of(new WorkflowError(ReplicateStatusCause.INPUT_FILES_DOWNLOAD_FAILED))); assertThat(isErrorWritten).isTrue(); String errorFileAsString = FileHelper.readFile(tmp + "/" @@ -144,7 +145,7 @@ void shouldNotWriteErrorToIexecOutSince() { boolean isErrorWritten = resultService.writeErrorToIexecOut(CHAIN_TASK_ID, ReplicateStatus.DATA_DOWNLOAD_FAILED, - List.of(ReplicateStatusCause.INPUT_FILES_DOWNLOAD_FAILED)); + List.of(new WorkflowError(ReplicateStatusCause.INPUT_FILES_DOWNLOAD_FAILED))); assertThat(isErrorWritten).isFalse(); } @@ -156,8 +157,8 @@ void shouldWriteMultipleErrorsToIexecOut() { final boolean isErrorWritten = resultService.writeErrorToIexecOut(CHAIN_TASK_ID, ReplicateStatus.DATA_DOWNLOAD_FAILED, - List.of(ReplicateStatusCause.INPUT_FILES_DOWNLOAD_FAILED, - ReplicateStatusCause.DATASET_FILE_DOWNLOAD_FAILED)); + List.of(new WorkflowError(ReplicateStatusCause.INPUT_FILES_DOWNLOAD_FAILED), + new WorkflowError(ReplicateStatusCause.DATASET_FILE_DOWNLOAD_FAILED))); assertThat(isErrorWritten).isTrue(); final String errorFileAsString = FileHelper.readFile(tmp + "/" diff --git a/src/test/java/com/iexec/worker/task/TaskManagerServiceTests.java b/src/test/java/com/iexec/worker/task/TaskManagerServiceTests.java index c12135cd..5fc9a1ab 100644 --- a/src/test/java/com/iexec/worker/task/TaskManagerServiceTests.java +++ b/src/test/java/com/iexec/worker/task/TaskManagerServiceTests.java @@ -42,7 +42,8 @@ import com.iexec.worker.sms.SmsService; import com.iexec.worker.tee.TeeService; import com.iexec.worker.tee.TeeServicesManager; -import com.iexec.worker.utils.WorkflowException; +import com.iexec.worker.workflow.WorkflowError; +import com.iexec.worker.workflow.WorkflowException; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -114,7 +115,7 @@ TaskDescription.TaskDescriptionBuilder getTaskDescriptionBuilder(boolean isTeeTa .dealParams(dealParams); } - final List emptyCauses = new ArrayList<>(); + final List emptyCauses = new ArrayList<>(); //region start @Test @@ -131,7 +132,7 @@ void shouldStartStandardTask() { @Test void shouldNotStartSinceCannotContributeStatusIsPresent() { when(contributionService.getCannotContributeStatusCause(CHAIN_TASK_ID)) - .thenReturn(List.of(CONTRIBUTION_TIMEOUT)); + .thenReturn(List.of(new WorkflowError(CONTRIBUTION_TIMEOUT))); ReplicateActionResponse actionResponse = taskManagerService.start(getTaskDescriptionBuilder(false).build()); @@ -175,7 +176,8 @@ void shouldNotStartSinceTeePrerequisitesAreNotMet() { when(contributionService.getCannotContributeStatusCause(CHAIN_TASK_ID)) .thenReturn(emptyCauses); when(teeServicesManager.getTeeService(any())).thenReturn(teeMockedService); - when(teeMockedService.areTeePrerequisitesMetForTask(CHAIN_TASK_ID)).thenReturn(List.of(TEE_NOT_SUPPORTED)); + when(teeMockedService.areTeePrerequisitesMetForTask(CHAIN_TASK_ID)) + .thenReturn(List.of(new WorkflowError(TEE_NOT_SUPPORTED))); ReplicateActionResponse actionResponse = taskManagerService.start(getTaskDescriptionBuilder(true).build()); @@ -204,7 +206,7 @@ void shouldDownloadApp() { @Test void shouldNotDownloadAppSinceCannotContributionStatusIsPresent() { when(contributionService.getCannotContributeStatusCause(CHAIN_TASK_ID)) - .thenReturn(List.of(CONTRIBUTION_TIMEOUT)); + .thenReturn(List.of(new WorkflowError(CONTRIBUTION_TIMEOUT))); ReplicateActionResponse actionResponse = taskManagerService.downloadApp(getTaskDescriptionBuilder(false).build()); @@ -262,7 +264,8 @@ void shouldAppDownloadFailedAndTriggerPostComputeHookWithFailure2() { when(resultService.writeErrorToIexecOut(anyString(), any(), any())) .thenReturn(true); when(computeManagerService.runPostCompute(taskDescription, null)) - .thenReturn(PostComputeResponse.builder().exitCauses(List.of(POST_COMPUTE_FAILED_UNKNOWN_ISSUE)).build()); + .thenReturn(PostComputeResponse.builder() + .exitCauses(List.of(new WorkflowError(POST_COMPUTE_FAILED_UNKNOWN_ISSUE))).build()); ReplicateActionResponse actionResponse = taskManagerService.downloadApp(taskDescription); @@ -282,7 +285,7 @@ void shouldAppDownloadFailedAndTriggerPostComputeHookWithFailure2() { void shouldNotDownloadDataSinceCannotContributeStatusIsPresent() { final TaskDescription taskDescription = getTaskDescriptionBuilder(false).build(); when(contributionService.getCannotContributeStatusCause(CHAIN_TASK_ID)) - .thenReturn(List.of(CONTRIBUTION_TIMEOUT)); + .thenReturn(List.of(new WorkflowError(CONTRIBUTION_TIMEOUT))); ReplicateActionResponse actionResponse = taskManagerService.downloadData(taskDescription); @@ -438,7 +441,8 @@ void shouldHandleDatasetDownloadFailureAndTriggerPostComputeHookWithFailure2() when(resultService.writeErrorToIexecOut(anyString(), any(), any())) .thenReturn(true); when(computeManagerService.runPostCompute(taskDescription, null)) - .thenReturn(PostComputeResponse.builder().exitCauses(List.of(POST_COMPUTE_FAILED_UNKNOWN_ISSUE)).build()); + .thenReturn(PostComputeResponse.builder() + .exitCauses(List.of(new WorkflowError(POST_COMPUTE_FAILED_UNKNOWN_ISSUE))).build()); ReplicateActionResponse actionResponse = taskManagerService.downloadData(taskDescription); @@ -569,7 +573,8 @@ void shouldWithInputFilesDataDownloadFailedAndTriggerPostComputeHookWithFailure2 when(resultService.writeErrorToIexecOut(anyString(), any(), any())) .thenReturn(true); when(computeManagerService.runPostCompute(taskDescription, null)) - .thenReturn(PostComputeResponse.builder().exitCauses(List.of(POST_COMPUTE_FAILED_UNKNOWN_ISSUE)).build()); + .thenReturn(PostComputeResponse.builder() + .exitCauses(List.of(new WorkflowError(POST_COMPUTE_FAILED_UNKNOWN_ISSUE))).build()); ReplicateActionResponse actionResponse = taskManagerService.downloadData(taskDescription); @@ -659,7 +664,7 @@ void shouldComputeTeeTask() { "CONTRIBUTION_ALREADY_SET", "WORKERPOOL_AUTHORIZATION_NOT_FOUND"}) void shouldNotComputeSinceCannotContributeStatusIsPresent(ReplicateStatusCause replicateStatusCause) { when(contributionService.getCannotContributeStatusCause(CHAIN_TASK_ID)) - .thenReturn(List.of(replicateStatusCause)); + .thenReturn(List.of(new WorkflowError(replicateStatusCause))); ReplicateActionResponse replicateActionResponse = taskManagerService.compute(getTaskDescriptionBuilder(false).build()); @@ -702,7 +707,7 @@ void shouldNotComputeSinceFailedPreCompute() { .thenReturn(workerpoolAuthorization); when(computeManagerService.runPreCompute(any(), any())) .thenReturn(PreComputeResponse.builder() - .exitCauses(List.of(PRE_COMPUTE_DATASET_URL_MISSING)) + .exitCauses(List.of(new WorkflowError(PRE_COMPUTE_DATASET_URL_MISSING))) .build()); ReplicateActionResponse replicateActionResponse = @@ -751,7 +756,7 @@ void shouldNotComputeSinceFailedAppCompute() { .thenReturn(PreComputeResponse.builder().build()); when(computeManagerService.runCompute(any(), any())) .thenReturn(AppComputeResponse.builder() - .exitCauses(List.of(APP_COMPUTE_FAILED)) + .exitCauses(List.of(new WorkflowError(APP_COMPUTE_FAILED))) .exitCode(5) .stdout("stdout") .build()); @@ -788,7 +793,7 @@ void shouldNotComputeSinceFailedPostCompute() { .thenReturn(AppComputeResponse.builder().stdout("stdout").build()); when(computeManagerService.runPostCompute(any(), any())) .thenReturn(PostComputeResponse.builder() - .exitCauses(List.of(POST_COMPUTE_FAILED_UNKNOWN_ISSUE)) + .exitCauses(List.of(new WorkflowError(POST_COMPUTE_FAILED_UNKNOWN_ISSUE))) .build()); @@ -831,7 +836,7 @@ void shouldContribute() { @Test void shouldNotContributeSinceCannotContributeStatusIsPresent() { when(contributionService.getCannotContributeStatusCause(CHAIN_TASK_ID)) - .thenReturn(List.of(CONTRIBUTION_TIMEOUT)); + .thenReturn(List.of(new WorkflowError(CONTRIBUTION_TIMEOUT))); ReplicateActionResponse replicateActionResponse = taskManagerService.contribute(CHAIN_TASK_ID); @@ -1154,7 +1159,7 @@ void shouldNotUploadResultSinceEmptyResultLink() { @Test void shouldNotContributeAndFinalizeSinceCannotContributeStatusIsPresent() { when(contributionService.getCannotContributeStatusCause(CHAIN_TASK_ID)) - .thenReturn(List.of(CONTRIBUTION_TIMEOUT)); + .thenReturn(List.of(new WorkflowError(CONTRIBUTION_TIMEOUT))); ReplicateActionResponse replicateActionResponse = taskManagerService.contributeAndFinalize(CHAIN_TASK_ID); assertThat(replicateActionResponse) .isNotNull() @@ -1209,7 +1214,8 @@ void shouldNotContributeAdnFinalizeSinceTrustNotOne() { when(iexecHubService.hasEnoughGas()).thenReturn(true); when(resultService.getComputedFile(CHAIN_TASK_ID)).thenReturn(computedFile); when(contributionService.getContribution(computedFile)).thenReturn(contribution); - when(contributionService.getCannotContributeAndFinalizeStatusCause(CHAIN_TASK_ID)).thenReturn(List.of(TRUST_NOT_1)); + when(contributionService.getCannotContributeAndFinalizeStatusCause(CHAIN_TASK_ID)) + .thenReturn(List.of(new WorkflowError(TRUST_NOT_1))); ReplicateActionResponse replicateActionResponse = taskManagerService.contributeAndFinalize(CHAIN_TASK_ID); assertThat(replicateActionResponse) .isNotNull() @@ -1227,7 +1233,8 @@ void shouldNotContributeAndFinalizeSinceTaskAlreadyContributed() { when(iexecHubService.hasEnoughGas()).thenReturn(true); when(resultService.getComputedFile(CHAIN_TASK_ID)).thenReturn(computedFile); when(contributionService.getContribution(computedFile)).thenReturn(contribution); - when(contributionService.getCannotContributeAndFinalizeStatusCause(CHAIN_TASK_ID)).thenReturn(List.of(TASK_ALREADY_CONTRIBUTED)); + when(contributionService.getCannotContributeAndFinalizeStatusCause(CHAIN_TASK_ID)) + .thenReturn(List.of(new WorkflowError(TASK_ALREADY_CONTRIBUTED))); ReplicateActionResponse replicateActionResponse = taskManagerService.contributeAndFinalize(CHAIN_TASK_ID); assertThat(replicateActionResponse) .isNotNull() diff --git a/src/test/java/com/iexec/worker/tee/TeeServiceTests.java b/src/test/java/com/iexec/worker/tee/TeeServiceTests.java index 060e0f60..aa645ea3 100644 --- a/src/test/java/com/iexec/worker/tee/TeeServiceTests.java +++ b/src/test/java/com/iexec/worker/tee/TeeServiceTests.java @@ -20,6 +20,7 @@ import com.iexec.sms.api.SmsClientCreationException; import com.iexec.worker.sgx.SgxService; import com.iexec.worker.sms.SmsService; +import com.iexec.worker.workflow.WorkflowError; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -87,7 +88,7 @@ void shouldTeePrerequisitesNotBeMetSinceTeeNotEnabled() { when(teeService.isTeeEnabled()).thenReturn(false); assertThat(teeService.areTeePrerequisitesMetForTask(CHAIN_TASK_ID)) - .containsExactly(TEE_NOT_SUPPORTED); + .containsExactly(new WorkflowError(TEE_NOT_SUPPORTED)); } @Test @@ -96,7 +97,7 @@ void shouldTeePrerequisitesNotBeMetSinceSmsClientCantBeLoaded() { when(smsService.getSmsClient(CHAIN_TASK_ID)).thenThrow(SmsClientCreationException.class); assertThat(teeService.areTeePrerequisitesMetForTask(CHAIN_TASK_ID)) - .containsExactly(UNKNOWN_SMS); + .containsExactly(new WorkflowError(UNKNOWN_SMS)); } @Test @@ -106,7 +107,7 @@ void shouldTeePrerequisitesNotBeMetSinceTeeEnclaveConfigurationIsNull() { when(teeServicesPropertiesService.getTeeServicesProperties(CHAIN_TASK_ID)).thenThrow(NullPointerException.class); assertThat(teeService.areTeePrerequisitesMetForTask(CHAIN_TASK_ID)) - .containsExactly(PRE_COMPUTE_MISSING_ENCLAVE_CONFIGURATION); + .containsExactly(new WorkflowError(PRE_COMPUTE_MISSING_ENCLAVE_CONFIGURATION)); } @Test @@ -116,7 +117,7 @@ void shouldTeePrerequisitesNotBeMetSinceTeeWorkflowConfigurationCantBeLoaded() { when(teeServicesPropertiesService.getTeeServicesProperties(CHAIN_TASK_ID)).thenThrow(RuntimeException.class); assertThat(teeService.areTeePrerequisitesMetForTask(CHAIN_TASK_ID)) - .containsExactly(GET_TEE_SERVICES_CONFIGURATION_FAILED); + .containsExactly(new WorkflowError(GET_TEE_SERVICES_CONFIGURATION_FAILED)); } // endregion } diff --git a/src/test/java/com/iexec/worker/tee/scone/TeeSconeServiceTests.java b/src/test/java/com/iexec/worker/tee/scone/TeeSconeServiceTests.java index 6c0ed326..ed4bdbc2 100644 --- a/src/test/java/com/iexec/worker/tee/scone/TeeSconeServiceTests.java +++ b/src/test/java/com/iexec/worker/tee/scone/TeeSconeServiceTests.java @@ -16,7 +16,6 @@ package com.iexec.worker.tee.scone; -import com.iexec.common.replicate.ReplicateStatusCause; import com.iexec.commons.poco.chain.IexecHubAbstractService; import com.iexec.commons.poco.task.TaskDescription; import com.iexec.commons.poco.tee.TeeEnclaveConfiguration; @@ -28,6 +27,7 @@ import com.iexec.worker.sgx.SgxService; import com.iexec.worker.sms.SmsService; import com.iexec.worker.tee.TeeServicesPropertiesService; +import com.iexec.worker.workflow.WorkflowError; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; @@ -112,7 +112,7 @@ void shouldTeePrerequisiteMetForTask() { doReturn(null).when(teeServicesPropertiesService).getTeeServicesProperties(CHAIN_TASK_ID); doReturn(true).when(teeSconeService).prepareTeeForTask(CHAIN_TASK_ID); - final List teePrerequisitesIssue = + final List teePrerequisitesIssue = teeSconeService.areTeePrerequisitesMetForTask(CHAIN_TASK_ID); assertThat(teePrerequisitesIssue).isEmpty(); @@ -127,11 +127,11 @@ void shouldTeePrerequisiteMetForTask() { void shouldTeePrerequisiteNotMetForTaskSinceTeeNotEnabled() { doReturn(false).when(teeSconeService).isTeeEnabled(); - final List teePrerequisitesIssue = + final List teePrerequisitesIssue = teeSconeService.areTeePrerequisitesMetForTask(CHAIN_TASK_ID); assertThat(teePrerequisitesIssue) - .containsExactly(TEE_NOT_SUPPORTED); + .containsExactly(new WorkflowError(TEE_NOT_SUPPORTED)); verify(teeSconeService, times(1)).isTeeEnabled(); verify(smsService, times(0)).getSmsClient(CHAIN_TASK_ID); @@ -145,11 +145,11 @@ void shouldTeePrerequisiteNotMetForTaskSinceSmsClientCantBeLoaded() { doReturn(TASK_DESCRIPTION).when(iexecHubService).getTaskDescription(CHAIN_TASK_ID); doThrow(SmsClientCreationException.class).when(smsService).getSmsClient(CHAIN_TASK_ID); - final List teePrerequisitesIssue = + final List teePrerequisitesIssue = teeSconeService.areTeePrerequisitesMetForTask(CHAIN_TASK_ID); assertThat(teePrerequisitesIssue) - .containsExactly(UNKNOWN_SMS); + .containsExactly(new WorkflowError(UNKNOWN_SMS)); verify(teeSconeService, times(1)).isTeeEnabled(); verify(smsService, times(1)).getSmsClient(CHAIN_TASK_ID); @@ -164,11 +164,11 @@ void shouldTeePrerequisiteNotMetForTaskSinceTeeWorkflowConfigurationCantBeLoaded doReturn(smsClient).when(smsService).getSmsClient(CHAIN_TASK_ID); doThrow(SmsClientCreationException.class).when(teeServicesPropertiesService).getTeeServicesProperties(CHAIN_TASK_ID); - final List teePrerequisitesIssue = + final List teePrerequisitesIssue = teeSconeService.areTeePrerequisitesMetForTask(CHAIN_TASK_ID); assertThat(teePrerequisitesIssue) - .containsExactly(GET_TEE_SERVICES_CONFIGURATION_FAILED); + .containsExactly(new WorkflowError(GET_TEE_SERVICES_CONFIGURATION_FAILED)); verify(teeSconeService, times(1)).isTeeEnabled(); verify(smsService, times(1)).getSmsClient(CHAIN_TASK_ID); @@ -184,11 +184,11 @@ void shouldTeePrerequisiteNotMetForTaskSinceCantPrepareTee() { doReturn(null).when(teeServicesPropertiesService).getTeeServicesProperties(CHAIN_TASK_ID); doReturn(false).when(teeSconeService).prepareTeeForTask(CHAIN_TASK_ID); - final List teePrerequisitesIssue = + final List teePrerequisitesIssue = teeSconeService.areTeePrerequisitesMetForTask(CHAIN_TASK_ID); assertThat(teePrerequisitesIssue) - .containsExactly(TEE_PREPARATION_FAILED); + .containsExactly(new WorkflowError(TEE_PREPARATION_FAILED)); verify(teeSconeService, times(1)).isTeeEnabled(); verify(smsService, times(1)).getSmsClient(CHAIN_TASK_ID);