diff --git a/gradle.properties b/gradle.properties index a72872aca..90c6d730e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ version=7.1.0 -iexecCommonVersion=6.0.0 +iexecCommonVersion=6.0.1-NEXT-SNAPSHOT iexecBlockchainAdapterVersion=7.1.1 iexecResultVersion=7.1.0 -iexecSmsVersion=7.1.0 +iexecSmsVersion=7.1.0-NEXT-SNAPSHOT nexusUser nexusPassword diff --git a/src/main/java/com/iexec/core/configuration/PurgeConfiguration.java b/src/main/java/com/iexec/core/configuration/PurgeConfiguration.java new file mode 100644 index 000000000..65e97caa2 --- /dev/null +++ b/src/main/java/com/iexec/core/configuration/PurgeConfiguration.java @@ -0,0 +1,27 @@ +package com.iexec.core.configuration; + +import com.iexec.common.lifecycle.purge.PurgeService; +import com.iexec.common.lifecycle.purge.Purgeable; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +@Configuration +@Slf4j +public class PurgeConfiguration { + /** + * Creates a {@link PurgeService} bean, with a list of all {@link Purgeable} beans as a parameter. + *

+ * If no {@link Purgeable} bean is known, then an empty list is passed as a parameter. + * This is a special case of Spring IoC, please see + * Spring documentation. + * @param purgeableServices List of services that can be purged on a task completion + * @return An instance of {@link PurgeService} containing a list of all {@link Purgeable} beans. + */ + @Bean + PurgeService purgeService(List purgeableServices) { + return new PurgeService(purgeableServices); + } +} diff --git a/src/main/java/com/iexec/core/configuration/SmsConfiguration.java b/src/main/java/com/iexec/core/configuration/SmsConfiguration.java deleted file mode 100644 index b0cb2bd89..000000000 --- a/src/main/java/com/iexec/core/configuration/SmsConfiguration.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2020 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.core.configuration; - -import com.iexec.sms.api.SmsClient; -import com.iexec.sms.api.SmsClientBuilder; -import feign.Logger; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; - -@Component -@Getter -@AllArgsConstructor -@NoArgsConstructor -public class SmsConfiguration { - - @Value("${sms.protocol}") - private String protocol; - - @Value("${sms.host}") - private String host; - - @Value("${sms.port}") - private String port; - - public String getSmsURL() { - return protocol + "://" + host + ":" + port; - } - - @Bean - public SmsClient smsClient() { - return SmsClientBuilder.getInstance(Logger.Level.NONE, getSmsURL()); - } -} diff --git a/src/main/java/com/iexec/core/replicate/ReplicateSupplyService.java b/src/main/java/com/iexec/core/replicate/ReplicateSupplyService.java index 60ada9cbf..205b90fcd 100644 --- a/src/main/java/com/iexec/core/replicate/ReplicateSupplyService.java +++ b/src/main/java/com/iexec/core/replicate/ReplicateSupplyService.java @@ -17,6 +17,8 @@ package com.iexec.core.replicate; import com.iexec.common.chain.WorkerpoolAuthorization; +import com.iexec.common.lifecycle.purge.ExpiringTaskMapFactory; +import com.iexec.common.lifecycle.purge.Purgeable; import com.iexec.common.notification.TaskNotification; import com.iexec.common.notification.TaskNotificationExtra; import com.iexec.common.notification.TaskNotificationType; @@ -33,22 +35,19 @@ import com.iexec.core.task.update.TaskUpdateRequestManager; import com.iexec.core.worker.Worker; import com.iexec.core.worker.WorkerService; -import net.jodah.expiringmap.ExpiringMap; import org.springframework.dao.OptimisticLockingFailureException; import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Service; import java.util.*; -import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import static com.iexec.common.replicate.ReplicateStatus.*; -import static com.iexec.core.task.Task.LONGEST_TASK_TIMEOUT; @Service -public class ReplicateSupplyService { +public class ReplicateSupplyService implements Purgeable { private final ReplicatesService replicatesService; private final SignatureService signatureService; @@ -56,10 +55,7 @@ public class ReplicateSupplyService { private final TaskUpdateRequestManager taskUpdateRequestManager; private final WorkerService workerService; private final Web3jService web3jService; - final Map taskAccessForNewReplicateLocks = - ExpiringMap.builder() - .expiration(LONGEST_TASK_TIMEOUT.getSeconds(), TimeUnit.SECONDS) - .build(); + final Map taskAccessForNewReplicateLocks = ExpiringTaskMapFactory.getExpiringTaskMap(); public ReplicateSupplyService(ReplicatesService replicatesService, SignatureService signatureService, @@ -532,4 +528,17 @@ private TaskAbortCause getTaskAbortCause(Task task) { return TaskAbortCause.UNKNOWN; } } + + // region purge locks + @Override + public boolean purgeTask(String chainTaskId) { + taskAccessForNewReplicateLocks.remove(chainTaskId); + return !taskAccessForNewReplicateLocks.containsKey(chainTaskId); + } + + @Override + public void purgeAllTasksData() { + taskAccessForNewReplicateLocks.clear(); + } + // endregion } diff --git a/src/main/java/com/iexec/core/sms/SmsClientProviderConfiguration.java b/src/main/java/com/iexec/core/sms/SmsClientProviderConfiguration.java new file mode 100644 index 000000000..da6b53e63 --- /dev/null +++ b/src/main/java/com/iexec/core/sms/SmsClientProviderConfiguration.java @@ -0,0 +1,14 @@ +package com.iexec.core.sms; + +import com.iexec.sms.api.SmsClientProvider; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SmsClientProviderConfiguration { + + @Bean + SmsClientProvider smsClientProvider() { + return new SmsClientProvider(); + } +} diff --git a/src/main/java/com/iexec/core/sms/SmsService.java b/src/main/java/com/iexec/core/sms/SmsService.java index 7df4bb4d0..fb24f0fcc 100644 --- a/src/main/java/com/iexec/core/sms/SmsService.java +++ b/src/main/java/com/iexec/core/sms/SmsService.java @@ -16,8 +16,15 @@ package com.iexec.core.sms; +import com.iexec.common.chain.ChainDeal; +import com.iexec.common.chain.IexecHubAbstractService; +import com.iexec.common.task.TaskDescription; +import com.iexec.common.tee.TeeEnclaveProvider; +import com.iexec.common.tee.TeeUtils; import com.iexec.common.utils.BytesUtils; import com.iexec.sms.api.SmsClient; +import com.iexec.sms.api.SmsClientCreationException; +import com.iexec.sms.api.SmsClientProvider; import feign.FeignException; import lombok.extern.slf4j.Slf4j; import org.springframework.retry.annotation.Recover; @@ -30,10 +37,63 @@ @Slf4j @Service public class SmsService { - private final SmsClient smsClient; + private final SmsClientProvider smsClientProvider; + private final IexecHubAbstractService iexecHubService; - public SmsService(SmsClient smsClient) { - this.smsClient = smsClient; + public SmsService(SmsClientProvider smsClientProvider, IexecHubAbstractService iexecHubService) { + this.smsClientProvider = smsClientProvider; + this.iexecHubService = iexecHubService; + } + + /** + * Checks the following conditions: + *

+ *

+ * If any of these conditions is wrong, then the {@link SmsClient} is considered to be not-ready. + * + * @param chainDealId ID of the on-chain deal related to the task to execute. + * @param chainTaskId ID of the on-chain task. + * @return {@literal true} if previous conditions are met, {@literal false} otherwise. + */ + public boolean isSmsClientReady(String chainDealId, String chainTaskId) { + try { + final Optional chainDeal = iexecHubService.getChainDeal(chainDealId); + if (chainDeal.isEmpty()) { + log.error("No chain deal for given ID [chainDealId: {}]", chainDealId); + return false; + } + final SmsClient smsClient = smsClientProvider.getOrCreateSmsClientForUninitializedTask(chainDeal.get(), chainTaskId); + final TeeEnclaveProvider teeEnclaveProviderForDeal = TeeUtils.getTeeEnclaveProvider(chainDeal.get().getTag()); + return checkSmsTeeEnclaveProvider(smsClient, teeEnclaveProviderForDeal, chainTaskId); + } catch (SmsClientCreationException e) { + log.error("SmsClient is not ready [chainTaskId: {}]", chainTaskId, e); + return false; + } + } + + private boolean checkSmsTeeEnclaveProvider(SmsClient smsClient, + TeeEnclaveProvider teeEnclaveProviderForDeal, + String chainTaskId) { + final TeeEnclaveProvider smsTeeEnclaveProvider; + try { + smsTeeEnclaveProvider = smsClient.getTeeEnclaveProvider(); + } catch (FeignException e) { + log.error("Can't retrieve SMS TEE enclave provider [chainTaskId:{}]", + chainTaskId, e); + return false; + } + + if (smsTeeEnclaveProvider != teeEnclaveProviderForDeal) { + log.error("SMS is configured for another TEE enclave provider " + + "[chainTaskId:{}, teeEnclaveProviderForDeal:{}, smsTeeEnclaveProvider:{}]", + chainTaskId, teeEnclaveProviderForDeal, smsTeeEnclaveProvider); + return false; + } + return true; } public Optional getEnclaveChallenge(String chainTaskId, boolean isTeeEnabled) { @@ -44,8 +104,14 @@ public Optional getEnclaveChallenge(String chainTaskId, boolean isTeeEna @Retryable(value = FeignException.class) Optional generateEnclaveChallenge(String chainTaskId) { + final TaskDescription taskDescription = iexecHubService.getTaskDescription(chainTaskId); + + // SMS client should already have been created once before. + // If it couldn't be created, then the task would have been aborted. + // So the following won't throw an exception. + final SmsClient smsClient = smsClientProvider.getOrCreateSmsClientForTask(taskDescription); - String teeChallengePublicKey = smsClient.generateTeeChallenge(chainTaskId); + final String teeChallengePublicKey = smsClient.generateTeeChallenge(chainTaskId); if (teeChallengePublicKey == null || teeChallengePublicKey.isEmpty()) { log.error("An error occurred while getting teeChallengePublicKey [chainTaskId:{}]", chainTaskId); diff --git a/src/main/java/com/iexec/core/task/Task.java b/src/main/java/com/iexec/core/task/Task.java index cee3b77f2..384da36a2 100644 --- a/src/main/java/com/iexec/core/task/Task.java +++ b/src/main/java/com/iexec/core/task/Task.java @@ -58,10 +58,6 @@ public class Task { public static final String CURRENT_STATUS_FIELD_NAME = "currentStatus"; public static final String CONTRIBUTION_DEADLINE_FIELD_NAME = "contributionDeadline"; - /** - * An XL task timeout happens after 100 hours. - */ - public static final Duration LONGEST_TASK_TIMEOUT = Duration.ofHours(100); @Id private String id; diff --git a/src/main/java/com/iexec/core/task/TaskRepository.java b/src/main/java/com/iexec/core/task/TaskRepository.java index 5f799cd01..766014baf 100644 --- a/src/main/java/com/iexec/core/task/TaskRepository.java +++ b/src/main/java/com/iexec/core/task/TaskRepository.java @@ -56,13 +56,13 @@ interface TaskRepository extends MongoRepository { * * * @param statuses The task status should be one of this list. - * @param excludedTag The task tag should not be this tag + * @param excludedTags The task tag should not be one this tag list * - use {@literal null} if no tag should be excluded. * @param excludedChainTaskIds The chain task ID should not be one of this list. * @param sort How to prioritize tasks. * @return The first task matching with the criteria, according to the {@code sort} parameter. */ - Optional findFirstByCurrentStatusInAndTagNotAndChainTaskIdNotIn(List statuses, String excludedTag, List excludedChainTaskIds, Sort sort); + Optional findFirstByCurrentStatusInAndTagNotInAndChainTaskIdNotIn(List statuses, List excludedTags, List excludedChainTaskIds, Sort sort); @Query("{ 'currentStatus': {$nin: ?0} }") List findByCurrentStatusNotIn(List statuses); diff --git a/src/main/java/com/iexec/core/task/TaskService.java b/src/main/java/com/iexec/core/task/TaskService.java index 55a04c29e..637ae4564 100644 --- a/src/main/java/com/iexec/core/task/TaskService.java +++ b/src/main/java/com/iexec/core/task/TaskService.java @@ -26,10 +26,7 @@ import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; -import java.util.Arrays; -import java.util.Date; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; import static com.iexec.core.task.TaskStatus.*; @@ -149,19 +146,19 @@ public List findByCurrentStatus(List statusList) { public Optional getPrioritizedInitializedOrRunningTask( boolean shouldExcludeTeeTasks, List excludedChainTaskIds) { - final String excludedTag = shouldExcludeTeeTasks - ? TeeUtils.TEE_TAG + final List excludedTags = shouldExcludeTeeTasks + ? List.of(TeeUtils.TEE_SCONE_ONLY_TAG, TeeUtils.TEE_GRAMINE_ONLY_TAG) : null; return findPrioritizedTask( Arrays.asList(INITIALIZED, RUNNING), - excludedTag, + excludedTags, excludedChainTaskIds, Sort.by(Sort.Order.desc(Task.CURRENT_STATUS_FIELD_NAME), Sort.Order.asc(Task.CONTRIBUTION_DEADLINE_FIELD_NAME))); } /** - * Shortcut for {@link TaskRepository#findFirstByCurrentStatusInAndTagNotAndChainTaskIdNotIn}. + * Shortcut for {@link TaskRepository#findFirstByCurrentStatusInAndTagNotInAndChainTaskIdNotIn}. * Retrieves the prioritized task matching with given criteria: *