From fbda9764b1ec6717020a723220dbe027e31a9b1b Mon Sep 17 00:00:00 2001 From: Michael Herdt Date: Mon, 3 Apr 2023 09:13:00 +0200 Subject: [PATCH] Fix circular rollout dependencies (#1337) * Do some refactoring to fix dependencies between rollout management, executor and evaluator beans. * Move rollout retrieving in same transaction as execution. * Do some refactoring. Extend logging and exception handling. * Remove unnecessary transactional and validation annotations. * remove catching never thrown bean * Fix new rollout handling API --- ...ssageDispatcherServiceIntegrationTest.java | 6 +- .../hawkbit/repository/RolloutHandler.java | 52 +++++++ .../hawkbit/repository/RolloutManagement.java | 41 ++---- .../repository/model/RolloutGroup.java | 60 +------- .../repository/jpa/JpaRolloutExecutor.java | 51 ++++--- .../repository/jpa/JpaRolloutHandler.java | 109 ++++++++++++++ .../repository/jpa/JpaRolloutManagement.java | 68 ++------- .../RepositoryApplicationConfiguration.java | 44 ++++-- .../jpa/rollout/RolloutScheduler.java | 22 +-- .../EvaluatorNotConfiguredException.java | 28 ++++ .../condition/PauseRolloutGroupAction.java | 8 +- .../RolloutGroupActionEvaluator.java | 8 +- .../RolloutGroupConditionEvaluator.java | 15 +- .../RolloutGroupEvaluationManager.java | 98 +++++++++++++ ...artNextGroupRolloutGroupSuccessAction.java | 8 +- .../ThresholdRolloutGroupErrorCondition.java | 10 +- ...ThresholdRolloutGroupSuccessCondition.java | 12 +- ...urrentDistributionSetInvalidationTest.java | 25 ++-- ...ributionSetInvalidationManagementTest.java | 6 +- .../jpa/RolloutGroupManagementTest.java | 6 +- .../repository/jpa/RolloutManagementTest.java | 136 +++++++++--------- .../jpa/event/RepositoryEntityEventTest.java | 2 +- .../test/util/AbstractIntegrationTest.java | 4 + .../repository/test/util/TestdataFactory.java | 12 +- .../rest/resource/MgmtActionResourceTest.java | 2 +- .../resource/MgmtRolloutResourceTest.java | 44 +++--- .../rest/resource/MgmtTargetResourceTest.java | 2 +- .../AbstractApiRestDocumentation.java | 2 +- .../RolloutResourceDocumentationTest.java | 8 +- 29 files changed, 537 insertions(+), 352 deletions(-) create mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutHandler.java create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutHandler.java create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/EvaluatorNotConfiguredException.java create mode 100644 hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/RolloutGroupEvaluationManager.java diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java index 46f711357..cb4015538 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java @@ -460,15 +460,11 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer return ds.getModules().stream().map(SoftwareModule::getId).collect(Collectors.toSet()); } - private Rollout createAndStartRollout(final DistributionSet ds, final String filterQuery) { - return createAndStartRollout(ds, filterQuery, null); - } - private Rollout createAndStartRollout(final DistributionSet ds, final String filterQuery, final Integer weight) { final Rollout rollout = testdataFactory.createRolloutByVariables(UUID.randomUUID().toString(), "", 1, filterQuery, ds, "50", "5", ActionType.FORCED, weight, false); rolloutManagement.start(rollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); return rollout; } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutHandler.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutHandler.java new file mode 100644 index 000000000..bd8086da1 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutHandler.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2023 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository; + +import static org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; + +import org.eclipse.hawkbit.repository.model.Rollout; +import org.eclipse.hawkbit.repository.model.RolloutGroup; +import org.eclipse.hawkbit.repository.model.Target; +import org.springframework.security.access.prepost.PreAuthorize; + +/** + * Represents the handler service for creating, deleting, and starting a Rollout + */ +@FunctionalInterface +public interface RolloutHandler { + + /** + * Process rollout based on its current {@link Rollout#getStatus()}. + * + * For {@link Rollout.RolloutStatus#CREATING} that means creating the + * {@link RolloutGroup}s with {@link Target}s and when finished switch to + * {@link Rollout.RolloutStatus#READY}. + * + * For {@link Rollout.RolloutStatus#READY} that means switching to + * {@link Rollout.RolloutStatus#STARTING} if the {@link Rollout#getStartAt()} is + * set and time of calling this method is beyond this point in time. This auto + * start mechanism is optional. Call {@link RolloutManagement#start(long)} otherwise. + * + * For {@link Rollout.RolloutStatus#STARTING} that means starting the first + * {@link RolloutGroup}s in line and when finished switch to + * {@link Rollout.RolloutStatus#RUNNING}. + * + * For {@link Rollout.RolloutStatus#RUNNING} that means checking to activate + * further groups based on the defined thresholds. Switched to + * {@link Rollout.RolloutStatus#FINISHED} is all groups are finished. + * + * For {@link Rollout.RolloutStatus#DELETING} that means either soft delete in + * case rollout was already {@link Rollout.RolloutStatus#RUNNING} which results + * in status change {@link Rollout.RolloutStatus#DELETED} or hard delete from + * the persistence otherwise. + * + */ + @PreAuthorize(SpringEvalExpressions.IS_SYSTEM_CODE) + void handleAll(); +} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutManagement.java index ae6039e3c..d073993ba 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RolloutManagement.java @@ -33,7 +33,6 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus; import org.eclipse.hawkbit.repository.model.RolloutGroupConditions; import org.eclipse.hawkbit.repository.model.RolloutGroupsValidation; -import org.eclipse.hawkbit.repository.model.Target; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -48,37 +47,7 @@ import org.springframework.util.concurrent.ListenableFuture; public interface RolloutManagement { /** - * Process rollout based on its current {@link Rollout#getStatus()}. - * - * For {@link RolloutStatus#CREATING} that means creating the - * {@link RolloutGroup}s with {@link Target}s and when finished switch to - * {@link RolloutStatus#READY}. - * - * For {@link RolloutStatus#READY} that means switching to - * {@link RolloutStatus#STARTING} if the {@link Rollout#getStartAt()} is set - * and time of calling this method is beyond this point in time. This auto - * start mechanism is optional. Call {@link #start(Long)} otherwise. - * - * For {@link RolloutStatus#STARTING} that means starting the first - * {@link RolloutGroup}s in line and when finished switch to - * {@link RolloutStatus#RUNNING}. - * - * For {@link RolloutStatus#RUNNING} that means checking to activate further - * groups based on the defined thresholds. Switched to - * {@link RolloutStatus#FINISHED} is all groups are finished. - * - * For {@link RolloutStatus#DELETING} that means either soft delete in case - * rollout was already {@link RolloutStatus#RUNNING} which results in status - * change {@link RolloutStatus#DELETED} or hard delete from the persistence - * otherwise. - * - */ - @PreAuthorize(SpringEvalExpressions.IS_SYSTEM_CODE) - void handleRollouts(); - - /** - * Counts all {@link Rollout}s in the repository that are not marked as - * deleted. + * Counts all {@link Rollout}s in the repository that are not marked as deleted. * * @return number of roll outs */ @@ -267,6 +236,14 @@ public interface RolloutManagement { Slice findByFiltersWithDetailedStatus(@NotNull Pageable pageable, @NotEmpty String searchText, boolean deleted); + /** + * Find rollouts which are still active and needs to be handled. + * + * @return a list of active rollouts + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ) + List findActiveRollouts(); + /** * Retrieves a specific rollout by its ID. * diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/RolloutGroup.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/RolloutGroup.java index aff0eefe9..ac5ee31fd 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/RolloutGroup.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/RolloutGroup.java @@ -157,40 +157,14 @@ public interface RolloutGroup extends NamedEntity { * The condition to evaluate if an group is success state. */ enum RolloutGroupSuccessCondition { - THRESHOLD("thresholdRolloutGroupSuccessCondition"); - - private final String beanName; - - RolloutGroupSuccessCondition(final String beanName) { - this.beanName = beanName; - } - - /** - * @return the beanName - */ - public String getBeanName() { - return beanName; - } + THRESHOLD } /** * The condition to evaluate if an group is in error state. */ enum RolloutGroupErrorCondition { - THRESHOLD("thresholdRolloutGroupErrorCondition"); - - private final String beanName; - - RolloutGroupErrorCondition(final String beanName) { - this.beanName = beanName; - } - - /** - * @return the beanName - */ - public String getBeanName() { - return beanName; - } + THRESHOLD } /** @@ -198,20 +172,7 @@ public interface RolloutGroup extends NamedEntity { * hit. */ enum RolloutGroupErrorAction { - PAUSE("pauseRolloutGroupAction"); - - private final String beanName; - - RolloutGroupErrorAction(final String beanName) { - this.beanName = beanName; - } - - /** - * @return the beanName - */ - public String getBeanName() { - return beanName; - } + PAUSE } /** @@ -219,19 +180,6 @@ public interface RolloutGroup extends NamedEntity { * is hit. */ enum RolloutGroupSuccessAction { - NEXTGROUP("startNextRolloutGroupAction"); - - private final String beanName; - - RolloutGroupSuccessAction(final String beanName) { - this.beanName = beanName; - } - - /** - * @return the beanName - */ - public String getBeanName() { - return beanName; - } + NEXTGROUP } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java index 06a99bd44..82e875739 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java @@ -34,8 +34,8 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaRollout; import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup; import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup; -import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupActionEvaluator; -import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupConditionEvaluator; +import org.eclipse.hawkbit.repository.jpa.rollout.condition.EvaluatorNotConfiguredException; +import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupEvaluationManager; import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.model.Action; @@ -53,8 +53,6 @@ import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder; import org.eclipse.hawkbit.tenancy.TenantAware; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; @@ -105,8 +103,9 @@ public class JpaRolloutExecutor implements RolloutExecutor { private final EventPublisherHolder eventPublisherHolder; private final PlatformTransactionManager txManager; private final RolloutApprovalStrategy rolloutApprovalStrategy; - private final ApplicationContext context; - + private final RolloutGroupEvaluationManager evaluationManager; + private final RolloutManagement rolloutManagement; + /** * Constructor */ @@ -117,7 +116,8 @@ public class JpaRolloutExecutor implements RolloutExecutor { final RolloutGroupManagement rolloutGroupManagement, final QuotaManagement quotaManagement, final DeploymentManagement deploymentManagement, final TargetManagement targetManagement, final EventPublisherHolder eventPublisherHolder, final PlatformTransactionManager txManager, - final RolloutApprovalStrategy rolloutApprovalStrategy, final ApplicationContext context) { + final RolloutApprovalStrategy rolloutApprovalStrategy, + final RolloutGroupEvaluationManager evaluationManager, final RolloutManagement rolloutManagement) { this.rolloutTargetGroupRepository = rolloutTargetGroupRepository; this.entityManager = entityManager; this.rolloutRepository = rolloutRepository; @@ -132,7 +132,8 @@ public class JpaRolloutExecutor implements RolloutExecutor { this.eventPublisherHolder = eventPublisherHolder; this.txManager = txManager; this.rolloutApprovalStrategy = rolloutApprovalStrategy; - this.context = context; + this.evaluationManager = evaluationManager; + this.rolloutManagement = rolloutManagement; } @Override @@ -285,7 +286,7 @@ public class JpaRolloutExecutor implements RolloutExecutor { LOGGER.debug( "handleReadyRollout called for rollout {} with autostart beyond define time. Switch to STARTING", rollout.getId()); - context.getBean(RolloutManagement.class).start(rollout.getId()); + rolloutManagement.start(rollout.getId()); } } @@ -417,11 +418,10 @@ public class JpaRolloutExecutor implements RolloutExecutor { private void callErrorAction(final Rollout rollout, final RolloutGroup rolloutGroup) { try { - context.getBean(rolloutGroup.getErrorAction().getBeanName(), RolloutGroupActionEvaluator.class) - .eval(rollout, rolloutGroup, rolloutGroup.getErrorActionExp()); - } catch (final BeansException e) { - LOGGER.error("Something bad happend when accessing the error action bean {}", - rolloutGroup.getErrorAction().getBeanName(), e); + evaluationManager.getErrorActionEvaluator(rolloutGroup.getErrorAction()).exec(rollout, rolloutGroup); + } catch (final EvaluatorNotConfiguredException e) { + LOGGER.error("Something bad happened when accessing the error action bean {}", + rolloutGroup.getErrorAction().name(), e); } } @@ -443,11 +443,10 @@ public class JpaRolloutExecutor implements RolloutExecutor { return false; } try { - return context.getBean(errorCondition.getBeanName(), RolloutGroupConditionEvaluator.class).eval(rollout, - rolloutGroup, rolloutGroup.getErrorConditionExp()); - } catch (final BeansException e) { - LOGGER.error("Something bad happend when accessing the error condition bean {}", - errorCondition.getBeanName(), e); + return evaluationManager.getErrorConditionEvaluator(errorCondition).eval(rollout, rolloutGroup, + rolloutGroup.getErrorConditionExp()); + } catch (final EvaluatorNotConfiguredException e) { + LOGGER.error("Something bad happened when accessing the error condition bean {}", errorCondition.name(), e); return false; } } @@ -456,9 +455,8 @@ public class JpaRolloutExecutor implements RolloutExecutor { final RolloutGroupSuccessCondition finishCondition) { LOGGER.trace("Checking finish condition {} on rolloutgroup {}", finishCondition, rolloutGroup); try { - final boolean isFinished = context - .getBean(finishCondition.getBeanName(), RolloutGroupConditionEvaluator.class) - .eval(rollout, rolloutGroup, rolloutGroup.getSuccessConditionExp()); + final boolean isFinished = evaluationManager.getSuccessConditionEvaluator(finishCondition).eval(rollout, + rolloutGroup, rolloutGroup.getSuccessConditionExp()); if (isFinished) { LOGGER.debug("Rolloutgroup {} is finished, starting next group", rolloutGroup); executeRolloutGroupSuccessAction(rollout, rolloutGroup); @@ -466,16 +464,15 @@ public class JpaRolloutExecutor implements RolloutExecutor { LOGGER.debug("Rolloutgroup {} is still running", rolloutGroup); } return isFinished; - } catch (final BeansException e) { - LOGGER.error("Something bad happend when accessing the finish condition bean {}", - finishCondition.getBeanName(), e); + } catch (final EvaluatorNotConfiguredException e) { + LOGGER.error("Something bad happened when accessing the finish condition or success action bean {}", + finishCondition.name(), e); return false; } } private void executeRolloutGroupSuccessAction(final Rollout rollout, final RolloutGroup rolloutGroup) { - context.getBean(rolloutGroup.getSuccessAction().getBeanName(), RolloutGroupActionEvaluator.class).eval(rollout, - rolloutGroup, rolloutGroup.getSuccessActionExp()); + evaluationManager.getSuccessActionEvaluator(rolloutGroup.getSuccessAction()).exec(rollout, rolloutGroup); } private void startFirstRolloutGroup(final Rollout rollout) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutHandler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutHandler.java new file mode 100644 index 000000000..b8e36fbaa --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutHandler.java @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2023 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository.jpa; + +import java.util.List; +import java.util.Objects; +import java.util.concurrent.locks.Lock; + +import org.eclipse.hawkbit.repository.RolloutExecutor; +import org.eclipse.hawkbit.repository.RolloutHandler; +import org.eclipse.hawkbit.repository.RolloutManagement; +import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper; +import org.eclipse.hawkbit.repository.model.BaseEntity; +import org.eclipse.hawkbit.tenancy.TenantAware; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.integration.support.locks.LockRegistry; +import org.springframework.transaction.PlatformTransactionManager; + +/** + * JPA implementation of {@link RolloutHandler}. + */ +public class JpaRolloutHandler implements RolloutHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(JpaRolloutHandler.class); + + private final TenantAware tenantAware; + private final RolloutManagement rolloutManagement; + private final RolloutExecutor rolloutExecutor; + private final LockRegistry lockRegistry; + private final PlatformTransactionManager txManager; + + /** + * Constructor + * + * @param tenantAware + * the {@link TenantAware} bean holding the tenant information + * @param rolloutManagement + * to fetch rollout related information from the datasource + * @param rolloutExecutor + * to trigger executions for a specific rollout + * @param lockRegistry + * to lock processes + * @param txManager + * transaction manager interface + */ + public JpaRolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement, + final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry, + final PlatformTransactionManager txManager) { + this.tenantAware = tenantAware; + this.rolloutManagement = rolloutManagement; + this.rolloutExecutor = rolloutExecutor; + this.lockRegistry = lockRegistry; + this.txManager = txManager; + } + + @Override + public void handleAll() { + final List rollouts = rolloutManagement.findActiveRollouts(); + + if (rollouts.isEmpty()) { + return; + } + + final String handlerId = createRolloutLockKey(tenantAware.getCurrentTenant()); + final Lock lock = lockRegistry.obtain(handlerId); + if (!lock.tryLock()) { + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Could not perform lock {}", lock); + } + return; + } + + try { + LOGGER.trace("Trigger handling {} rollouts.", rollouts.size()); + rollouts.forEach(rolloutId -> handleRolloutInNewTransaction(rolloutId, handlerId)); + } finally { + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("Unlock lock {}", lock); + } + lock.unlock(); + } + } + + private static String createRolloutLockKey(final String tenant) { + return tenant + "-rollout"; + } + + private void handleRolloutInNewTransaction(final long rolloutId, final String handlerId) { + DeploymentHelper.runInNewTransaction(txManager, handlerId + "-" + rolloutId, status -> { + rolloutManagement.get(rolloutId).ifPresentOrElse( + rollout -> runInUserContext(rollout, () -> rolloutExecutor.execute(rollout)), + () -> LOGGER.error("Could not retrieve rollout with id {}. Will not continue with execution.", + rolloutId)); + return 0L; + }); + } + + private void runInUserContext(final BaseEntity rollout, final Runnable handler) { + DeploymentHelper.runInNonSystemContext(handler, () -> Objects.requireNonNull(rollout.getCreatedBy()), + tenantAware); + } + +} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java index be3065ae2..0e5d6939c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutManagement.java @@ -16,9 +16,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; -import java.util.concurrent.locks.Lock; import java.util.function.Function; import java.util.stream.Collectors; @@ -28,7 +26,6 @@ import javax.validation.ValidationException; import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.QuotaManagement; import org.eclipse.hawkbit.repository.RolloutApprovalStrategy; -import org.eclipse.hawkbit.repository.RolloutExecutor; import org.eclipse.hawkbit.repository.RolloutFields; import org.eclipse.hawkbit.repository.RolloutHelper; import org.eclipse.hawkbit.repository.RolloutManagement; @@ -52,10 +49,8 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_; import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRolloutGroupSuccessAction; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification; -import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.jpa.utils.WeightValidationHelper; -import org.eclipse.hawkbit.repository.model.BaseEntity; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.Rollout; @@ -70,8 +65,6 @@ import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus; import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.security.SystemSecurityContext; -import org.eclipse.hawkbit.tenancy.TenantAware; -import org.eclipse.hawkbit.utils.TenantConfigHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -82,14 +75,11 @@ import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.jpa.domain.Specification; -import org.springframework.integration.support.locks.LockRegistry; import org.springframework.orm.jpa.vendor.Database; import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Retryable; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.AsyncResult; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.concurrent.ListenableFuture; @@ -137,35 +127,26 @@ public class JpaRolloutManagement implements RolloutManagement { private final TargetManagement targetManagement; private final DistributionSetManagement distributionSetManagement; private final VirtualPropertyReplacer virtualPropertyReplacer; - private final PlatformTransactionManager txManager; - private final TenantAware tenantAware; - private final LockRegistry lockRegistry; private final RolloutApprovalStrategy rolloutApprovalStrategy; private final TenantConfigurationManagement tenantConfigurationManagement; private final SystemSecurityContext systemSecurityContext; - private final RolloutExecutor rolloutExecutor; private final EventPublisherHolder eventPublisherHolder; private final Database database; public JpaRolloutManagement(final TargetManagement targetManagement, final DistributionSetManagement distributionSetManagement, final EventPublisherHolder eventPublisherHolder, - final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager, - final TenantAware tenantAware, final LockRegistry lockRegistry, final Database database, + final VirtualPropertyReplacer virtualPropertyReplacer, final Database database, final RolloutApprovalStrategy rolloutApprovalStrategy, final TenantConfigurationManagement tenantConfigurationManagement, - final SystemSecurityContext systemSecurityContext, final RolloutExecutor rolloutExecutor) { + final SystemSecurityContext systemSecurityContext) { this.targetManagement = targetManagement; this.distributionSetManagement = distributionSetManagement; this.virtualPropertyReplacer = virtualPropertyReplacer; - this.txManager = txManager; - this.tenantAware = tenantAware; - this.lockRegistry = lockRegistry; this.rolloutApprovalStrategy = rolloutApprovalStrategy; this.tenantConfigurationManagement = tenantConfigurationManagement; this.systemSecurityContext = systemSecurityContext; this.eventPublisherHolder = eventPublisherHolder; this.database = database; - this.rolloutExecutor = rolloutExecutor; } @Override @@ -435,43 +416,10 @@ public class JpaRolloutManagement implements RolloutManagement { rolloutRepository.save(rollout); } - @Override - // No transaction, will be created per handled rollout - @Transactional(propagation = Propagation.NEVER) - public void handleRollouts() { - final List rollouts = rolloutRepository.findByStatusIn(ACTIVE_ROLLOUTS); - - if (rollouts.isEmpty()) { - return; - } - - final String tenant = tenantAware.getCurrentTenant(); - - final String handlerId = createRolloutLockKey(tenant); - final Lock lock = lockRegistry.obtain(handlerId); - if (!lock.tryLock()) { - return; - } - - try { - rollouts.forEach(rolloutId -> DeploymentHelper.runInNewTransaction(txManager, handlerId + "-" + rolloutId, - status -> handleRollout(rolloutId))); - } finally { - lock.unlock(); - } - } - public static String createRolloutLockKey(final String tenant) { return tenant + "-rollout"; } - private long handleRollout(final long rolloutId) { - final JpaRollout rollout = rolloutRepository.findById(rolloutId) - .orElseThrow(() -> new EntityNotFoundException(Rollout.class, rolloutId)); - runInUserContext(rollout, () -> rolloutExecutor.execute(rollout)); - return 0; - } - @Override @Transactional @Retryable(include = { @@ -517,6 +465,11 @@ public class JpaRolloutManagement implements RolloutManagement { return findAll; } + @Override + public List findActiveRollouts() { + return rolloutRepository.findByStatusIn(ACTIVE_ROLLOUTS); + } + @Override public Optional getByName(final String rolloutName) { return rolloutRepository.findByName(rolloutName); @@ -649,11 +602,6 @@ public class JpaRolloutManagement implements RolloutManagement { QuotaHelper.assertAssignmentQuota(requested, quota, Target.class, RolloutGroup.class); } - private void runInUserContext(final BaseEntity rollout, final Runnable handler) { - DeploymentHelper.runInNonSystemContext(handler, () -> Objects.requireNonNull(rollout.getCreatedBy()), - tenantAware); - } - @Override @Transactional public void cancelRolloutsForDistributionSet(final DistributionSet set) { @@ -778,7 +726,7 @@ public class JpaRolloutManagement implements RolloutManagement { .filter(g -> RolloutGroupStatus.RUNNING.equals(g.getStatus())).findFirst() .orElseThrow(() -> new RolloutIllegalStateException("No group is running")); - startNextRolloutGroupAction.eval(rollout, latestRunning, latestRunning.getSuccessActionExp()); + startNextRolloutGroupAction.exec(rollout, latestRunning); } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java index 82e730e97..47c19187a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java @@ -36,6 +36,7 @@ import org.eclipse.hawkbit.repository.RepositoryProperties; import org.eclipse.hawkbit.repository.RolloutApprovalStrategy; import org.eclipse.hawkbit.repository.RolloutExecutor; import org.eclipse.hawkbit.repository.RolloutGroupManagement; +import org.eclipse.hawkbit.repository.RolloutHandler; import org.eclipse.hawkbit.repository.RolloutManagement; import org.eclipse.hawkbit.repository.RolloutStatusCache; import org.eclipse.hawkbit.repository.SoftwareModuleManagement; @@ -84,6 +85,9 @@ import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHol import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder; import org.eclipse.hawkbit.repository.jpa.rollout.RolloutScheduler; import org.eclipse.hawkbit.repository.jpa.rollout.condition.PauseRolloutGroupAction; +import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupActionEvaluator; +import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupConditionEvaluator; +import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupEvaluationManager; import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRolloutGroupSuccessAction; import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupErrorCondition; import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupSuccessCondition; @@ -92,6 +96,7 @@ import org.eclipse.hawkbit.repository.jpa.rsql.RsqlParserValidationOracle; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.Rollout; +import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; @@ -119,7 +124,6 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration; import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers; -import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @@ -195,6 +199,16 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { return new ThresholdRolloutGroupSuccessCondition(actionRepository); } + @Bean + RolloutGroupEvaluationManager evaluationManager( + final List> errorConditionEvaluators, + final List> successConditionEvaluators, + final List> errorActionEvaluators, + final List> successActionEvaluators) { + return new RolloutGroupEvaluationManager(errorConditionEvaluators, successConditionEvaluators, + errorActionEvaluators, successActionEvaluators); + } + @Bean @ConditionalOnMissingBean SystemManagementCacheKeyGenerator systemManagementCacheKeyGenerator() { @@ -660,6 +674,14 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { return new JpaSoftwareModuleTypeManagement(distributionSetTypeRepository, softwareModuleTypeRepository, virtualPropertyReplacer, softwareModuleRepository, properties.getDatabase()); } + + @Bean + @ConditionalOnMissingBean + RolloutHandler rolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement, + final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry, + final PlatformTransactionManager txManager) { + return new JpaRolloutHandler(tenantAware, rolloutManagement, rolloutExecutor, lockRegistry, txManager); + } @Bean @ConditionalOnMissingBean @@ -670,25 +692,25 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { final RolloutGroupManagement rolloutGroupManagement, final QuotaManagement quotaManagement, final DeploymentManagement deploymentManagement, final TargetManagement targetManagement, final EventPublisherHolder eventPublisherHolder, final PlatformTransactionManager txManager, - final RolloutApprovalStrategy rolloutApprovalStrategy, final ApplicationContext context) { + final RolloutApprovalStrategy rolloutApprovalStrategy, + final RolloutGroupEvaluationManager evaluationManager, final RolloutManagement rolloutManagement) { return new JpaRolloutExecutor(rolloutTargetGroupRepository, entityManager, rolloutRepository, actionRepository, rolloutGroupRepository, afterCommit, tenantAware, rolloutGroupManagement, quotaManagement, deploymentManagement, targetManagement, eventPublisherHolder, txManager, rolloutApprovalStrategy, - context); + evaluationManager, rolloutManagement); } @Bean @ConditionalOnMissingBean RolloutManagement rolloutManagement(final TargetManagement targetManagement, final DistributionSetManagement distributionSetManagement, final EventPublisherHolder eventPublisherHolder, - final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager, - final TenantAware tenantAware, final LockRegistry lockRegistry, final JpaProperties properties, + final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties, final RolloutApprovalStrategy rolloutApprovalStrategy, final TenantConfigurationManagement tenantConfigurationManagement, - final SystemSecurityContext systemSecurityContext, final RolloutExecutor rolloutExecutor) { + final SystemSecurityContext systemSecurityContext) { return new JpaRolloutManagement(targetManagement, distributionSetManagement, eventPublisherHolder, - virtualPropertyReplacer, txManager, tenantAware, lockRegistry, properties.getDatabase(), - rolloutApprovalStrategy, tenantConfigurationManagement, systemSecurityContext, rolloutExecutor); + virtualPropertyReplacer, properties.getDatabase(), rolloutApprovalStrategy, + tenantConfigurationManagement, systemSecurityContext); } /** @@ -913,7 +935,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { * * @param systemManagement * to find all tenants - * @param rolloutManagement + * @param rolloutHandler * to run the rollout handler * @param systemSecurityContext * to run as system @@ -924,8 +946,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { @Profile("!test") @ConditionalOnProperty(prefix = "hawkbit.rollout.scheduler", name = "enabled", matchIfMissing = true) RolloutScheduler rolloutScheduler(final TenantAware tenantAware, final SystemManagement systemManagement, - final RolloutManagement rolloutManagement, final SystemSecurityContext systemSecurityContext) { - return new RolloutScheduler(systemManagement, rolloutManagement, systemSecurityContext); + final RolloutHandler rolloutHandler, final SystemSecurityContext systemSecurityContext) { + return new RolloutScheduler(systemManagement, rolloutHandler, systemSecurityContext); } /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/RolloutScheduler.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/RolloutScheduler.java index 914a2c2ca..f72ffccf2 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/RolloutScheduler.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/RolloutScheduler.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.repository.jpa.rollout; -import org.eclipse.hawkbit.repository.RolloutManagement; +import org.eclipse.hawkbit.repository.RolloutHandler; import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.security.SystemSecurityContext; import org.slf4j.Logger; @@ -16,8 +16,8 @@ import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; /** - * Scheduler to schedule the {@link RolloutManagement#handleRollouts()}. The - * delay between the checks be be configured using the property from + * Scheduler to schedule the {@link RolloutHandler#handleAll()}. The + * delay between the checks be configured using the property from * {#PROP_SCHEDULER_DELAY_PLACEHOLDER}. */ public class RolloutScheduler { @@ -28,7 +28,7 @@ public class RolloutScheduler { private final SystemManagement systemManagement; - private final RolloutManagement rolloutManagement; + private final RolloutHandler rolloutHandler; private final SystemSecurityContext systemSecurityContext; @@ -37,22 +37,22 @@ public class RolloutScheduler { * * @param systemManagement * to find all tenants - * @param rolloutManagement + * @param rolloutHandler * to run the rollout handler * @param systemSecurityContext * to run as system */ - public RolloutScheduler(final SystemManagement systemManagement, final RolloutManagement rolloutManagement, + public RolloutScheduler(final SystemManagement systemManagement, final RolloutHandler rolloutHandler, final SystemSecurityContext systemSecurityContext) { this.systemManagement = systemManagement; - this.rolloutManagement = rolloutManagement; + this.rolloutHandler = rolloutHandler; this.systemSecurityContext = systemSecurityContext; } /** * Scheduler method called by the spring-async mechanism. Retrieves all - * tenants from the {@link SystemManagement#findTenants()} and runs for each - * tenant the {@link RolloutManagement#handleRollouts()} in the + * tenants from the {@link SystemManagement#findTenants} and runs for each + * tenant the {@link RolloutHandler#handleAll()} in the * {@link SystemSecurityContext}. */ @Scheduled(initialDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER) @@ -63,13 +63,13 @@ public class RolloutScheduler { // permission to query and create entities. systemSecurityContext.runAsSystem(() -> { // workaround eclipselink that is currently not possible to - // execute a query without multitenancy if MultiTenant + // execute a query without multi-tenancy if MultiTenant // annotation is used. // https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So // iterate through all tenants and execute the rollout check for // each tenant seperately. - systemManagement.forEachTenant(tenant -> rolloutManagement.handleRollouts()); + systemManagement.forEachTenant(tenant -> rolloutHandler.handleAll()); return null; }); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/EvaluatorNotConfiguredException.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/EvaluatorNotConfiguredException.java new file mode 100644 index 000000000..fdbf4af0d --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/EvaluatorNotConfiguredException.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2023 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository.jpa.rollout.condition; + +/** + * Exception indicating that a specific evaluator is missing in the application + * context. + */ +public class EvaluatorNotConfiguredException extends RuntimeException { + + private static final String MESSAGE_FORMAT = "Cannot find any configured evaluator for action/condition '%s'. Please ensure to configure one in the application context to make use of it."; + + /** + * Constructor + * + * @param s + * the action/condition to evaluate + */ + public EvaluatorNotConfiguredException(final String s) { + super(String.format(MESSAGE_FORMAT, s)); + } +} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/PauseRolloutGroupAction.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/PauseRolloutGroupAction.java index 96011c12d..1250f15aa 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/PauseRolloutGroupAction.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/PauseRolloutGroupAction.java @@ -20,7 +20,7 @@ import org.eclipse.hawkbit.security.SystemSecurityContext; * Error action evaluator which pauses the whole {@link Rollout} and sets the * current {@link RolloutGroup} to error. */ -public class PauseRolloutGroupAction implements RolloutGroupActionEvaluator { +public class PauseRolloutGroupAction implements RolloutGroupActionEvaluator { private final RolloutManagement rolloutManagement; @@ -36,12 +36,12 @@ public class PauseRolloutGroupAction implements RolloutGroupActionEvaluator { } @Override - public boolean verifyExpression(final String expression) { - return true; + public RolloutGroup.RolloutGroupErrorAction getAction() { + return RolloutGroup.RolloutGroupErrorAction.PAUSE; } @Override - public void eval(final Rollout rollout, final RolloutGroup rolloutG, final String expression) { + public void exec(final Rollout rollout, final RolloutGroup rolloutG) { final JpaRolloutGroup rolloutGroup = (JpaRolloutGroup) rolloutG; systemSecurityContext.runAsSystem(() -> { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/RolloutGroupActionEvaluator.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/RolloutGroupActionEvaluator.java index d375e68c5..0df60add0 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/RolloutGroupActionEvaluator.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/RolloutGroupActionEvaluator.java @@ -12,11 +12,11 @@ import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.RolloutGroup; /** - * + * Interface to define a specific execution for an action */ -public interface RolloutGroupActionEvaluator { +public interface RolloutGroupActionEvaluator { - boolean verifyExpression(final String expression); + T getAction(); - void eval(Rollout rollout, RolloutGroup rolloutGroup, final String expression); + void exec(Rollout rollout, RolloutGroup rolloutGroup); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/RolloutGroupConditionEvaluator.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/RolloutGroupConditionEvaluator.java index 5575d7cff..b9b10fbb3 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/RolloutGroupConditionEvaluator.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/RolloutGroupConditionEvaluator.java @@ -12,20 +12,11 @@ import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.RolloutGroup; /** - * Verifies {@link RolloutGroup#getErrorConditionExp()}. + * Interface for evaluate conditions define for a {@link RolloutGroup} based on a given expression */ -@FunctionalInterface -public interface RolloutGroupConditionEvaluator { +public interface RolloutGroupConditionEvaluator { - default boolean verifyExpression(final String expression) { - // percentage value between 0 and 100 - try { - final Integer value = Integer.valueOf(expression); - return value >= 0 && value <= 100; - } catch (final NumberFormatException e) { - return false; - } - } + T getCondition(); boolean eval(Rollout rollout, RolloutGroup rolloutGroup, final String expression); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/RolloutGroupEvaluationManager.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/RolloutGroupEvaluationManager.java new file mode 100644 index 000000000..4c7cfe5fd --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/RolloutGroupEvaluationManager.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2023 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository.jpa.rollout.condition; + +import java.util.List; + +import org.eclipse.hawkbit.repository.model.RolloutGroup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Manager class to collect all instances of + * {@link RolloutGroupConditionEvaluator} and + * {@link RolloutGroupActionEvaluator} for specific conditions and actions. The + * corresponding instance can be fetched by providing the action/condition. + */ +public class RolloutGroupEvaluationManager { + + private static final Logger LOGGER = LoggerFactory.getLogger(RolloutGroupEvaluationManager.class); + + private final List> errorConditionEvaluators; + private final List> successConditionEvaluators; + private final List> errorActionEvaluators; + private final List> successActionEvaluators; + + /** + * Constructor + * + * @param errorConditionEvaluators + * evaluators for instances of {@link RolloutGroupConditionEvaluator} + * handling the {@link RolloutGroup.RolloutGroupErrorCondition} + * @param successConditionEvaluators + * evaluators for instances of {@link RolloutGroupConditionEvaluator} + * handling the {@link RolloutGroup.RolloutGroupSuccessCondition} + * @param errorActionEvaluators + * evaluators for instances of {@link RolloutGroupActionEvaluator} + * handling the {@link RolloutGroup.RolloutGroupErrorAction} + * @param successActionEvaluators + * evaluators for instances of {@link RolloutGroupActionEvaluator} + * handling the {@link RolloutGroup.RolloutGroupSuccessAction} + */ + public RolloutGroupEvaluationManager( + final List> errorConditionEvaluators, + final List> successConditionEvaluators, + final List> errorActionEvaluators, + final List> successActionEvaluators) { + this.errorConditionEvaluators = errorConditionEvaluators; + this.successConditionEvaluators = successConditionEvaluators; + this.errorActionEvaluators = errorActionEvaluators; + this.successActionEvaluators = successActionEvaluators; + } + + public RolloutGroupActionEvaluator getErrorActionEvaluator( + final RolloutGroup.RolloutGroupErrorAction errorAction) { + return findFirstActionEvaluator(errorActionEvaluators, errorAction); + + } + + public RolloutGroupActionEvaluator getSuccessActionEvaluator( + final RolloutGroup.RolloutGroupSuccessAction successAction) { + return findFirstActionEvaluator(successActionEvaluators, successAction); + } + + public RolloutGroupConditionEvaluator getErrorConditionEvaluator( + final RolloutGroup.RolloutGroupErrorCondition errorCondition) { + return findFirstConditionEvaluator(errorConditionEvaluators, errorCondition); + + } + + public RolloutGroupConditionEvaluator getSuccessConditionEvaluator( + final RolloutGroup.RolloutGroupSuccessCondition successCondition) { + return findFirstConditionEvaluator(successConditionEvaluators, successCondition); + } + + private static > RolloutGroupActionEvaluator findFirstActionEvaluator( + final List> evaluators, final T action) { + return evaluators.stream().filter(evaluator -> evaluator.getAction() == action).findFirst().orElseThrow(() -> { + LOGGER.warn("Could not find suitable evaluator for the '{}' action.", action.name()); + throw new EvaluatorNotConfiguredException(action.name()); + }); + } + + private static > RolloutGroupConditionEvaluator findFirstConditionEvaluator( + final List> evaluators, final T condition) { + return evaluators.stream().filter(evaluator -> evaluator.getCondition() == condition).findFirst() + .orElseThrow(() -> { + LOGGER.warn("Could not find suitable evaluator for the '{}' condition.", condition.name()); + throw new EvaluatorNotConfiguredException(condition.name()); + }); + } + +} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/StartNextGroupRolloutGroupSuccessAction.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/StartNextGroupRolloutGroupSuccessAction.java index cfc3fcfba..a4d4f8cd0 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/StartNextGroupRolloutGroupSuccessAction.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/StartNextGroupRolloutGroupSuccessAction.java @@ -23,7 +23,7 @@ import org.slf4j.LoggerFactory; /** * Success action which starts the next following {@link RolloutGroup}. */ -public class StartNextGroupRolloutGroupSuccessAction implements RolloutGroupActionEvaluator { +public class StartNextGroupRolloutGroupSuccessAction implements RolloutGroupActionEvaluator { private static final Logger logger = LoggerFactory.getLogger(StartNextGroupRolloutGroupSuccessAction.class); @@ -41,12 +41,12 @@ public class StartNextGroupRolloutGroupSuccessAction implements RolloutGroupActi } @Override - public boolean verifyExpression(final String expression) { - return true; + public RolloutGroup.RolloutGroupSuccessAction getAction() { + return RolloutGroup.RolloutGroupSuccessAction.NEXTGROUP; } @Override - public void eval(final Rollout rollout, final RolloutGroup rolloutGroup, final String expression) { + public void exec(final Rollout rollout, final RolloutGroup rolloutGroup) { systemSecurityContext.runAsSystem(() -> { startNextGroup(rollout, rolloutGroup); return null; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupErrorCondition.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupErrorCondition.java index 797611c8a..730eaccf4 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupErrorCondition.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupErrorCondition.java @@ -18,9 +18,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * + * Evaluates if the {@link RolloutGroup#getErrorConditionExp()} is reached. */ -public class ThresholdRolloutGroupErrorCondition implements RolloutGroupConditionEvaluator { +public class ThresholdRolloutGroupErrorCondition + implements RolloutGroupConditionEvaluator { private static final Logger LOGGER = LoggerFactory.getLogger(ThresholdRolloutGroupErrorCondition.class); @@ -30,6 +31,11 @@ public class ThresholdRolloutGroupErrorCondition implements RolloutGroupConditio this.actionRepository = actionRepository; } + @Override + public RolloutGroup.RolloutGroupErrorCondition getCondition() { + return RolloutGroup.RolloutGroupErrorCondition.THRESHOLD; + } + @Override public boolean eval(final Rollout rollout, final RolloutGroup rolloutGroup, final String expression) { final Long totalGroup = actionRepository.countByRolloutAndRolloutGroup((JpaRollout) rollout, diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupSuccessCondition.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupSuccessCondition.java index a0b655968..149ef689a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupSuccessCondition.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/rollout/condition/ThresholdRolloutGroupSuccessCondition.java @@ -16,10 +16,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** - * Threshold to calculate if rollout group success condition is reached and the + * Threshold to calculate if the {@link RolloutGroup#getSuccessConditionExp()} is reached and the * next rollout group can get started. */ -public class ThresholdRolloutGroupSuccessCondition implements RolloutGroupConditionEvaluator { +public class ThresholdRolloutGroupSuccessCondition + implements RolloutGroupConditionEvaluator { private static final Logger LOGGER = LoggerFactory.getLogger(ThresholdRolloutGroupSuccessCondition.class); private final ActionRepository actionRepository; @@ -28,6 +29,11 @@ public class ThresholdRolloutGroupSuccessCondition implements RolloutGroupCondit this.actionRepository = actionRepository; } + @Override + public RolloutGroup.RolloutGroupSuccessCondition getCondition() { + return RolloutGroup.RolloutGroupSuccessCondition.THRESHOLD; + } + @Override public boolean eval(final Rollout rollout, final RolloutGroup rolloutGroup, final String expression) { @@ -44,7 +50,7 @@ public class ThresholdRolloutGroupSuccessCondition implements RolloutGroupCondit final long finished = this.actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rollout.getId(), rolloutGroup.getId(), completeActionStatus); try { - final Integer threshold = Integer.valueOf(expression); + final int threshold = Integer.parseInt(expression); // calculate threshold return ((float) finished / (float) totalGroup) >= ((float) threshold / 100F); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ConcurrentDistributionSetInvalidationTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ConcurrentDistributionSetInvalidationTest.java index 4cfc8d7c1..066baf4b9 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ConcurrentDistributionSetInvalidationTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ConcurrentDistributionSetInvalidationTest.java @@ -8,9 +8,15 @@ */ package org.eclipse.hawkbit.repository.jpa; -import io.qameta.allure.Description; -import io.qameta.allure.Feature; -import io.qameta.allure.Story; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.AdditionalAnswers.delegatesTo; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; + +import java.util.Collections; +import java.util.concurrent.TimeUnit; + import org.awaitility.Awaitility; import org.awaitility.Duration; import org.eclipse.hawkbit.repository.exception.StopRolloutException; @@ -32,14 +38,9 @@ import org.springframework.context.annotation.Primary; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; -import java.util.Collections; -import java.util.concurrent.TimeUnit; - -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.AdditionalAnswers.delegatesTo; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; +import io.qameta.allure.Description; +import io.qameta.allure.Feature; +import io.qameta.allure.Story; /** * Test class testing the invalidation of a {@link DistributionSet} while the @@ -87,7 +88,7 @@ public class ConcurrentDistributionSetInvalidationTest extends AbstractJpaIntegr // run in new Thread so that the invalidation can be executed in // parallel new Thread(() -> systemSecurityContext.runAsSystemAsTenant(() -> { - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); return 0; }, tenant)).start(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DistributionSetInvalidationManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DistributionSetInvalidationManagementTest.java index aac9c2356..35f490ee1 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DistributionSetInvalidationManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DistributionSetInvalidationManagementTest.java @@ -58,7 +58,7 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 0, 0); distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); assertThat(targetFilterQueryManagement.get(invalidationTestData.getTargetFilterQuery().getId()).get() .getAutoAssignDistributionSet()).isNull(); @@ -87,7 +87,7 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 0, 1); distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); assertThat(targetFilterQueryManagement.get(invalidationTestData.getTargetFilterQuery().getId()).get() .getAutoAssignDistributionSet()).isNull(); @@ -119,7 +119,7 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 5, 1); distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); assertThat(targetFilterQueryManagement.get(invalidationTestData.getTargetFilterQuery().getId()).get() .getAutoAssignDistributionSet()).isNull(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutGroupManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutGroupManagementTest.java index b50bc7aa7..0307bf40c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutGroupManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutGroupManagementTest.java @@ -92,7 +92,7 @@ class RolloutGroupManagementTest extends AbstractJpaIntegrationTest { .getContent(); final RolloutGroup rolloutGroup = rolloutGroups.get(0); rolloutManagement.pauseRollout(rollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final List targets = rolloutGroupManagement.findTargetsOfRolloutGroup(PAGE, rolloutGroup.getId()) .getContent(); Target targetCancelled = targets.get(0); @@ -195,7 +195,7 @@ class RolloutGroupManagementTest extends AbstractJpaIntegrationTest { final Rollout rollout = testdataFactory.createRollout(); final List rolloutGroups = rolloutGroupManagement.findByRollout(PAGE, rollout.getId()) .getContent(); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // check query when no actions exist final List targetsWithActionStatus = rolloutGroupManagement @@ -208,7 +208,7 @@ class RolloutGroupManagementTest extends AbstractJpaIntegrationTest { assertTargetNotNullAndActionStatusNullAndActionStatusCode(targetsWithActionStatus, null); rolloutManagement.start(rollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // check query when no action status code exist final List scheduledActions = findActionsByRolloutAndStatus(rollout, Status.SCHEDULED); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java index 9924c6ab9..df4105830 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java @@ -126,7 +126,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final Rollout rollout = testdataFactory.createRolloutByVariables("rolloutNotCancelRunningAction", "description", 1, "name==*", knownDistributionSet, "50", "5"); rolloutManagement.start(rollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // verify that manually created action is still running and action // created from rollout is finished @@ -163,7 +163,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final Rollout rollout = testdataFactory.createRolloutByVariables("rolloutNotCancelRunningAction", "description", 1, "name==*", knownDistributionSet, "50", "5", confirmationRequired); rolloutManagement.start(rollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // verify that manually created action is still running and action // created from rollout is finished @@ -201,7 +201,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final Rollout rollout = testdataFactory.createRolloutByVariables("rolloutNotCancelRunningAction", "description", 1, "name==*", secondDistributionSet, "50", "5"); rolloutManagement.start(rollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // verify that manually created action is canceled and action // created from rollout is running @@ -333,7 +333,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { // check running rollouts again, now the finish condition should be hit // and should start the next group - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // verify that now the first and the second group are in running state final List runningRolloutGroups = rolloutGroupManagement @@ -395,7 +395,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { @Step("Check the status of the rollout groups, second group should be in running status") private void checkSecondGroupStatusIsRunning(final Rollout createdRollout) { - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final List runningRolloutGroups = rolloutGroupManagement .findByRollout(new OffsetBasedPageRequest(0, 10, Sort.by(Direction.ASC, "id")), createdRollout.getId()) .getContent(); @@ -428,7 +428,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { @Step("Check the status of the rollout groups and the rollout") private void verifyRolloutAndAllGroupsAreFinished(final Rollout createdRollout) { - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final List runningRolloutGroups = rolloutGroupManagement .findByRollout(PAGE, createdRollout.getId()).getContent(); assertThat(runningRolloutGroups.get(0).getStatus()).isEqualTo(RolloutGroupStatus.FINISHED); @@ -467,7 +467,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { // check running rollouts again, now the error condition should be hit // and should execute the error action - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final Rollout rollout = reloadRollout(createdRollout); // the rollout itself should be in paused based on the error action @@ -510,7 +510,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { // check running rollouts again, now the error condition should be hit // and should execute the error action - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final Rollout rollout = reloadRollout(createdRollout); // the rollout itself should be in paused based on the error action @@ -529,7 +529,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { assertThat(reloadRollout(createdRollout).getStatus()).isEqualTo(RolloutStatus.RUNNING); // checking rollouts again - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // next group should be running again after resuming the rollout final List resumedGroups = rolloutGroupManagement @@ -557,7 +557,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { // calculate the rest of the groups and finish them for (int groupsLeft = amountGroups - 1; groupsLeft >= 1; groupsLeft--) { // next check and start next group - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // finish running actions, 2 actions should be finished assertThat(changeStatusForAllRunningActions(createdRollout, Status.FINISHED)).isEqualTo(2); assertThat(getRollout(createdRollout.getId()).getStatus()).isEqualTo(RolloutStatus.RUNNING); @@ -565,7 +565,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { } // check rollout to see that all actions and all groups are finished and // so can go to FINISHED state of the rollout - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // verify all groups are in finished state rolloutGroupManagement @@ -598,7 +598,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { rolloutManagement.start(createdRollout.getId()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // 6 targets are ready and 2 are running validationMap = createInitStatusMap(); @@ -607,7 +607,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { validateRolloutActionStatus(createdRollout.getId(), validationMap); changeStatusForAllRunningActions(createdRollout, Status.FINISHED); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // 4 targets are ready, 2 are finished and 2 are running validationMap = createInitStatusMap(); validationMap.put(TotalTargetCountStatus.Status.SCHEDULED, 4L); @@ -616,7 +616,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { validateRolloutActionStatus(createdRollout.getId(), validationMap); changeStatusForAllRunningActions(createdRollout, Status.FINISHED); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // 2 targets are ready, 4 are finished and 2 are running validationMap = createInitStatusMap(); validationMap.put(TotalTargetCountStatus.Status.SCHEDULED, 2L); @@ -625,7 +625,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { validateRolloutActionStatus(createdRollout.getId(), validationMap); changeStatusForAllRunningActions(createdRollout, Status.FINISHED); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // 0 targets are ready, 6 are finished and 2 are running validationMap = createInitStatusMap(); validationMap.put(TotalTargetCountStatus.Status.FINISHED, 6L); @@ -633,7 +633,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { validateRolloutActionStatus(createdRollout.getId(), validationMap); changeStatusForAllRunningActions(createdRollout, Status.FINISHED); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // 0 targets are ready, 8 are finished and 0 are running validationMap = createInitStatusMap(); validationMap.put(TotalTargetCountStatus.Status.FINISHED, 8L); @@ -662,7 +662,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { rolloutManagement.start(createdRollout.getId()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // 6 targets are ready and 2 are running validationMap = createInitStatusMap(); @@ -671,7 +671,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { validateRolloutActionStatus(createdRollout.getId(), validationMap); changeStatusForAllRunningActions(createdRollout, Status.DOWNLOADED); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // 4 targets are ready, 2 are finished(with DOWNLOADED action status) // and 2 are running validationMap = createInitStatusMap(); @@ -681,7 +681,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { validateRolloutActionStatus(createdRollout.getId(), validationMap); changeStatusForAllRunningActions(createdRollout, Status.DOWNLOADED); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // 2 targets are ready, 4 are finished(with DOWNLOADED action status) // and 2 are running validationMap = createInitStatusMap(); @@ -691,7 +691,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { validateRolloutActionStatus(createdRollout.getId(), validationMap); changeStatusForAllRunningActions(createdRollout, Status.DOWNLOADED); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // 0 targets are ready, 6 are finished(with DOWNLOADED action status) // and 2 are running validationMap = createInitStatusMap(); @@ -700,7 +700,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { validateRolloutActionStatus(createdRollout.getId(), validationMap); changeStatusForAllRunningActions(createdRollout, Status.FINISHED); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // 0 targets are ready, 6 are finished(with DOWNLOADED action status), 2 // are finished and 0 are running validationMap = createInitStatusMap(); @@ -730,7 +730,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { rolloutManagement.start(createdRollout.getId()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // 6 targets are ready and 2 are running validationMap = createInitStatusMap(); @@ -739,7 +739,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { validateRolloutActionStatus(createdRollout.getId(), validationMap); changeStatusForAllRunningActions(createdRollout, Status.ERROR); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // 6 targets are ready and 2 are error validationMap = createInitStatusMap(); validationMap.put(TotalTargetCountStatus.Status.SCHEDULED, 6L); @@ -761,7 +761,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { successCondition, errorCondition); changeStatusForAllRunningActions(createdRollout, Status.FINISHED); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // round(9/4)=2 targets finished (Group 1) // round(7/3)=2 targets running (Group 3) @@ -853,7 +853,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final Rollout rolloutTwo = testdataFactory.createRolloutByVariables("rolloutTwo", "This is the description for rollout two", 1, "controllerId==rollout-*", dsForRolloutTwo, "50", "80"); changeStatusForAllRunningActions(rolloutOne, Status.FINISHED); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // Verify that 5 targets are finished, 5 are running and 5 are ready. Map expectedTargetCountStatus = createInitStatusMap(); expectedTargetCountStatus.put(TotalTargetCountStatus.Status.RUNNING, 5L); @@ -864,7 +864,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { rolloutManagement.start(rolloutTwo.getId()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // Verify that 5 targets are finished, 5 are still running and 5 are // cancelled. @@ -892,13 +892,13 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { rolloutOne = reloadRollout(rolloutOne); changeStatusForRunningActions(rolloutOne, Status.ERROR, 2); changeStatusForRunningActions(rolloutOne, Status.FINISHED, 3); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); changeStatusForRunningActions(rolloutOne, Status.ERROR, 2); changeStatusForRunningActions(rolloutOne, Status.FINISHED, 3); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); changeStatusForRunningActions(rolloutOne, Status.ERROR, 2); changeStatusForRunningActions(rolloutOne, Status.FINISHED, 3); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // 9 targets are finished and 6 have error Map expectedTargetCountStatus = createInitStatusMap(); @@ -917,7 +917,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { rolloutManagement.start(rolloutTwo.getId()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); rolloutTwo = reloadRollout(rolloutTwo); // 6 error targets are now running @@ -950,7 +950,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { rolloutOne = reloadRollout(rolloutOne); changeStatusForRunningActions(rolloutOne, Status.ERROR, 2); changeStatusForRunningActions(rolloutOne, Status.FINISHED, 3); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // verify: 40% error but 60% finished -> should move to next group final List rolloutGroups = rolloutGroupManagement.findByRollout(PAGE, rolloutOne.getId()) .getContent(); @@ -976,7 +976,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { rolloutOne = reloadRollout(rolloutOne); changeStatusForRunningActions(rolloutOne, Status.ERROR, 2); changeStatusForRunningActions(rolloutOne, Status.FINISHED, 3); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // verify: 40% error and 60% finished -> should not move to next group // because successCondition 80% final List rolloutGruops = rolloutGroupManagement.findByRollout(PAGE, rolloutOne.getId()) @@ -1002,7 +1002,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { rolloutOne = reloadRollout(rolloutOne); changeStatusForRunningActions(rolloutOne, Status.ERROR, 2); changeStatusForRunningActions(rolloutOne, Status.FINISHED, 3); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // verify: 40% error -> should pause because errorCondition is 20% rolloutOne = reloadRollout(rolloutOne); assertThat(rolloutOne.getStatus()).isEqualTo(RolloutStatus.PAUSED); @@ -1019,39 +1019,39 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final Rollout rolloutA = createTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, amountGroups, successCondition, errorCondition, "RolloutA", "RolloutA"); rolloutManagement.start(rolloutA.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final int amountTargetsForRollout2 = 10; final int amountGroups2 = 2; final Rollout rolloutB = createTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout2, amountGroups2, successCondition, errorCondition, "RolloutB", "RolloutB"); rolloutManagement.start(rolloutB.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); changeStatusForAllRunningActions(rolloutB, Status.FINISHED); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final int amountTargetsForRollout3 = 10; final int amountGroups3 = 2; final Rollout rolloutC = createTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout3, amountGroups3, successCondition, errorCondition, "RolloutC", "RolloutC"); rolloutManagement.start(rolloutC.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); changeStatusForAllRunningActions(rolloutC, Status.ERROR); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final int amountTargetsForRollout4 = 15; final int amountGroups4 = 3; final Rollout rolloutD = createTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout4, amountGroups4, successCondition, errorCondition, "RolloutD", "RolloutD"); rolloutManagement.start(rolloutD.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); changeStatusForRunningActions(rolloutD, Status.ERROR, 1); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); changeStatusForAllRunningActions(rolloutD, Status.FINISHED); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final Slice rolloutPage = rolloutManagement .findAllWithDetailedStatus(new OffsetBasedPageRequest(0, 100, Sort.by(Direction.ASC, "name")), false); @@ -1181,10 +1181,10 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { rolloutManagement.start(myRollout.getId()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); changeStatusForRunningActions(myRollout, Status.FINISHED, 2); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); myRollout = reloadRollout(myRollout); float percent = rolloutGroupManagement @@ -1194,7 +1194,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { assertThat(percent).isEqualTo(40); changeStatusForRunningActions(myRollout, Status.FINISHED, 3); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); percent = rolloutGroupManagement .getWithDetailedStatus( @@ -1204,7 +1204,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { changeStatusForRunningActions(myRollout, Status.FINISHED, 4); changeStatusForAllRunningActions(myRollout, Status.ERROR); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); percent = rolloutGroupManagement .getWithDetailedStatus( @@ -1233,7 +1233,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { rolloutManagement.start(myRollout.getId()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final Condition targetBelongsInRollout = new Condition<>(s -> s.startsWith(rolloutName), "Target belongs into rollout"); @@ -1336,7 +1336,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { rolloutManagement.start(myRolloutId); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); awaitRunningState(myRolloutId); @@ -1365,7 +1365,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { assertThat(myRollout.getStatus()).isEqualTo(RolloutStatus.READY); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final Long myRolloutId = myRollout.getId(); myRollout = getRollout(myRolloutId); @@ -1374,7 +1374,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { rolloutManagement.start(myRolloutId); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); awaitRunningState(myRolloutId); @@ -1487,7 +1487,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final Long myRolloutId = myRollout.getId(); rolloutManagement .update(entityFactory.rollout().update(myRolloutId).startAt(System.currentTimeMillis() + 60000)); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // rollout should not have been started myRollout = getRollout(myRolloutId); @@ -1495,13 +1495,13 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { // schedule to now rolloutManagement.update(entityFactory.rollout().update(myRolloutId).startAt(System.currentTimeMillis())); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); myRollout = getRollout(myRolloutId); assertThat(myRollout.getStatus()).isEqualTo(RolloutStatus.STARTING); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); awaitRunningState(myRolloutId); @@ -1562,7 +1562,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { TimeUnit.SECONDS.sleep(1); testdataFactory.createTargets(10, rolloutName + "-notIn-", rolloutName); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); myRollout = getRollout(myRollout.getId()); assertThat(myRollout.getStatus()).isEqualTo(RolloutStatus.READY); @@ -1613,7 +1613,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { }); // first handle iteration will put rollout in ready state - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); assertThat(getRollout(rolloutId)).satisfies(rollout -> { assertThat(rollout.getStatus()).isEqualTo(RolloutStatus.READY); @@ -1629,7 +1629,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { // start rollout rolloutManagement.start(rolloutId); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // verify rollout started. Check groups are in right state. // Group 1 should be in WFC state, since confirmation is required here. @@ -1641,7 +1641,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { // cancel execution of all action of group 1 to trigger second group forceQuitAllActionsOfRolloutGroup(rolloutGroupIds.get(0)); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); assertRolloutGroup(rolloutGroupIds.get(0), RolloutGroupStatus.FINISHED, true, amountTargetsInGroup1, Status.CANCELED); assertRolloutGroup(rolloutGroupIds.get(1), RolloutGroupStatus.SCHEDULED, false, amountTargetsInGroup2, @@ -1649,7 +1649,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { // verify actions of second rule are directly in RUNNING state, since // confirmation is not required for this group - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); assertRolloutGroup(rolloutGroupIds.get(0), RolloutGroupStatus.FINISHED, true, amountTargetsInGroup1, Status.CANCELED); assertRolloutGroup(rolloutGroupIds.get(1), RolloutGroupStatus.RUNNING, false, amountTargetsInGroup2, @@ -1855,7 +1855,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { // test rolloutManagement.delete(createdRollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // verify final Optional deletedRollout = rolloutRepository.findById(createdRollout.getId()); @@ -1889,7 +1889,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { // start the rollout, so it has active running actions and a group which // has been started rolloutManagement.start(createdRollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // verify we have running actions assertThat(actionRepository.findByRolloutIdAndStatus(PAGE, createdRollout.getId(), Status.RUNNING) @@ -1897,7 +1897,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { // test rolloutManagement.delete(createdRollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // verify final JpaRollout deletedRollout = rolloutRepository.findById(createdRollout.getId()).get(); @@ -1941,11 +1941,11 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { Rollout rolloutRunning = rolloutManagement.create(rolloutRunningCreate, 1, false, conditions); // Let the executor handle created Rollout - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // start the rollout, so it has active running actions and a group which // has been started rolloutManagement.start(rolloutRunning.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); rolloutRunning = reloadRollout(rolloutRunning); final String prefixRolloutReady = randomString + "2"; @@ -1953,7 +1953,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { .name(prefixRolloutReady + "-testRollout").targetFilterQuery("name==" + randomString + "*").set(testDs); Rollout rolloutReady = rolloutManagement.create(rolloutReadyCreate, 1, false, conditions); // Let the executor handle created Rollout - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); rolloutReady = reloadRollout(rolloutReady); final List rolloutsOrderedByStatus = rolloutManagement @@ -2017,7 +2017,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { .createSimpleTestRolloutWithTargetsAndDistributionSet(amountOfTargets, 2, amountOfTargets, "80", "50", null, weight).getId(); rolloutManagement.start(rolloutId); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final List actions = deploymentManagement.findActionsAll(PAGE).getContent(); assertThat(actions) // .hasSize(amountOfTargets) // @@ -2034,7 +2034,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { enableMultiAssignments(); rolloutManagement.start(rolloutId); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final List actions = deploymentManagement.findActionsAll(PAGE).getContent(); assertThat(actions).hasSize(amountOfTargets).allMatch(action -> !action.getWeight().isPresent()); } @@ -2114,7 +2114,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final Rollout createdRollout = rolloutManagement.create(rolloutToCreate, 1, false, conditions); // Let the executor handle created Rollout - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final Rollout testRollout = reloadRollout(createdRollout); final List rolloutGroups = rolloutGroupManagement @@ -2175,8 +2175,6 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { } } - - private Rollout createTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout, final int groupSize, final String successCondition, final String errorCondition, final String rolloutName, final String targetPrefixName) { @@ -2300,7 +2298,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { .withMessageContaining(errorMessage); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final Rollout rollout = reloadRollout(createdRollout); rolloutManagement.pauseRollout(rollout.getId()); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/event/RepositoryEntityEventTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/event/RepositoryEntityEventTest.java index 4c47e92f0..ef70bf86a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/event/RepositoryEntityEventTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/event/RepositoryEntityEventTest.java @@ -142,7 +142,7 @@ public class RepositoryEntityEventTest extends AbstractJpaIntegrationTest { "controllerId==" + targetPrefixName + "-*", distributionSet, successCondition, errorCondition); rolloutManagement.delete(createdRollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final RolloutDeletedEvent rolloutDeletedEvent = eventListener.waitForEvent(RolloutDeletedEvent.class); assertThat(rolloutDeletedEvent).isNotNull(); diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java index b00b01b62..6e500def1 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/AbstractIntegrationTest.java @@ -39,6 +39,7 @@ import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.QuotaManagement; import org.eclipse.hawkbit.repository.RepositoryConstants; import org.eclipse.hawkbit.repository.RolloutGroupManagement; +import org.eclipse.hawkbit.repository.RolloutHandler; import org.eclipse.hawkbit.repository.RolloutManagement; import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; @@ -181,6 +182,9 @@ public abstract class AbstractIntegrationTest { @Autowired protected RolloutManagement rolloutManagement; + @Autowired + protected RolloutHandler rolloutHandler; + @Autowired protected RolloutGroupManagement rolloutGroupManagement; diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java index ec29be426..173423f08 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java @@ -36,6 +36,7 @@ import org.eclipse.hawkbit.repository.DistributionSetTagManagement; import org.eclipse.hawkbit.repository.DistributionSetTypeManagement; import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.QuotaManagement; +import org.eclipse.hawkbit.repository.RolloutHandler; import org.eclipse.hawkbit.repository.RolloutManagement; import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; @@ -177,6 +178,9 @@ public class TestdataFactory { @Autowired private RolloutManagement rolloutManagement; + @Autowired + private RolloutHandler rolloutHandler; + @Autowired private QuotaManagement quotaManagement; @@ -1202,7 +1206,7 @@ public class TestdataFactory { groupSize, confirmationRequired, conditions); // Run here, because Scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); return rolloutManagement.get(rollout.getId()).get(); } @@ -1253,7 +1257,7 @@ public class TestdataFactory { rolloutManagement.start(rollout.getId()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); return reloadRollout(rollout); } @@ -1350,9 +1354,9 @@ public class TestdataFactory { public Rollout createSoftDeletedRollout(final String prefix) { final Rollout newRollout = createRollout(prefix); rolloutManagement.start(newRollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); rolloutManagement.delete(newRollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); return newRollout; } diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtActionResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtActionResourceTest.java index e78e52a67..31ee64d6a 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtActionResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtActionResourceTest.java @@ -207,7 +207,7 @@ class MgmtActionResourceTest extends AbstractManagementApiIntegrationTest { final Rollout rollout = testdataFactory.createRolloutByVariables("TestRollout", "TestDesc", 1, "name==" + target1.getName(), ds, "50", "5"); rolloutManagement.start(rollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final String rsqlRolloutName = "rollout.name==" + rollout.getName(); final String rsqlRolloutId = "rollout.id==" + rollout.getId(); diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutResourceTest.java index ad0c58cc3..d97ee04b6 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtRolloutResourceTest.java @@ -301,9 +301,9 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { 4, false, new RolloutGroupConditionBuilder().withDefaults() .successCondition(RolloutGroupSuccessCondition.THRESHOLD, "100").build()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); rolloutManagement.start(rollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // request the list of rollouts with full representation mvc.perform(get("/rest/v1/rollouts?representation=full").accept(MediaType.APPLICATION_JSON)) @@ -458,7 +458,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { @Step private void retrieveAndVerifyRolloutInRunning(final Rollout rollout) throws Exception { - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); mvc.perform(get("/rest/v1/rollouts/" + rollout.getId()).accept(MediaType.APPLICATION_JSON)) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) @@ -494,7 +494,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { @Step private void retrieveAndVerifyRolloutInReady(final Rollout rollout) throws Exception { - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); mvc.perform(get("/rest/v1/rollouts/" + rollout.getId()).accept(MediaType.APPLICATION_JSON)) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) @@ -556,7 +556,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { postRollout("rollout2", 5, dsA.getId(), "id==target-0001*", 10, Action.ActionType.FORCED); // Run here, because Scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); mvc.perform(get("/rest/v1/rollouts").accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()).andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) @@ -598,7 +598,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { startAt, forcetime); // Run here, because Scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); retrieveAndCompareRolloutsContent(dsA, "/rest/v1/rollouts", false, true, startAt, forcetime); retrieveAndCompareRolloutsContent(dsA, "/rest/v1/rollouts?representation=full", true, true, startAt, forcetime); @@ -616,7 +616,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { postRollout("rollout2", 5, dsA.getId(), "id==target-0001*", 10, Action.ActionType.FORCED); // Run here, because Scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); retrieveAndCompareRolloutsContent(dsA, "/rest/v1/rollouts", false); retrieveAndCompareRolloutsContent(dsA, "/rest/v1/rollouts?representation=full", true); @@ -634,7 +634,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { postRollout("rollout2", 5, dsA.getId(), "id==target*", 20, Action.ActionType.FORCED); // Run here, because Scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); mvc.perform(get("/rest/v1/rollouts?limit=1").accept(MediaType.APPLICATION_JSON)) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) @@ -707,7 +707,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { .andExpect(jsonPath("status", equalTo("starting"))); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // check rollout is in running state mvc.perform(get("/rest/v1/rollouts/{rolloutId}", rollout.getId()).accept(MediaType.APPLICATION_JSON)) @@ -733,7 +733,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { .andExpect(status().isOk()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // pausing rollout mvc.perform(post("/rest/v1/rollouts/{rolloutId}/pause", rollout.getId())).andDo(MockMvcResultPrinter.print()) @@ -763,7 +763,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { .andExpect(status().isOk()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // pausing rollout mvc.perform(post("/rest/v1/rollouts/{rolloutId}/pause", rollout.getId())).andDo(MockMvcResultPrinter.print()) @@ -797,7 +797,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { .andExpect(status().isOk()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // starting rollout - already started should lead into bad request mvc.perform(post("/rest/v1/rollouts/{rolloutId}/start", rollout.getId())).andDo(MockMvcResultPrinter.print()) @@ -838,7 +838,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { .andExpect(status().isOk()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // retrieve rollout groups from created rollout - 2 groups exists // (amountTargets / groupSize = 2) @@ -891,7 +891,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { final RolloutGroup firstGroup, final RolloutGroup secondGroup, final boolean confirmationFlowEnabled, final boolean confirmationRequired) throws Exception { rolloutManagement.start(rollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); mvc.perform(get("/rest/v1/rollouts/{rolloutId}/deploygroups/{groupId}", rollout.getId(), firstGroup.getId()) .accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) @@ -923,7 +923,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { @Step private void retrieveAndVerifyRolloutGroupInReady(final Rollout rollout, final RolloutGroup firstGroup) throws Exception { - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); mvc.perform(get("/rest/v1/rollouts/{rolloutId}/deploygroups/{groupId}", rollout.getId(), firstGroup.getId()) .accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) @@ -1034,7 +1034,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { rolloutManagement.start(rollout.getId()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); final RolloutGroup firstGroup = rolloutGroupManagement .findByRollout(PageRequest.of(0, 1, Direction.ASC, "id"), rollout.getId()).getContent().get(0); @@ -1064,7 +1064,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { .andExpect(status().isOk()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // check if running awaitRunningState(rollout.getId()); @@ -1280,7 +1280,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { .successCondition(RolloutGroupSuccessCondition.THRESHOLD, "100").build()); // Run here, because Scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); return rolloutManagement.get(rollout.getId()).orElseThrow(NoSuchElementException::new); } @@ -1295,7 +1295,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { final Rollout rollout = createRollout("rollout1", 4, dsA.getId(), "controllerId==rollout*"); rolloutManagement.start(rollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); mvc.perform(post("/rest/v1/rollouts/{rolloutId}/triggerNextGroup", rollout.getId())) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); @@ -1318,7 +1318,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { triggerNextGroupAndExpect(rollout, status().isBadRequest()); // READY state - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); triggerNextGroupAndExpect(rollout, status().isBadRequest()); // STARTING state @@ -1326,7 +1326,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { triggerNextGroupAndExpect(rollout, status().isBadRequest()); // RUNNING state - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); triggerNextGroupAndExpect(rollout, status().isOk()); // PAUSED state @@ -1341,7 +1341,7 @@ class MgmtRolloutResourceTest extends AbstractManagementApiIntegrationTest { // FINISHED state setTargetsStatus(targets, Status.FINISHED); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); triggerNextGroupAndExpect(rollout, status().isBadRequest()); } diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java index 8fc570de6..dc7e69965 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java @@ -2118,7 +2118,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { final Rollout rollout = testdataFactory.createRolloutByVariables("My Rollout", "My Rollout Description", 1, "name==trg*", ds, "50", "5"); rolloutManagement.start(rollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); // get all actions for the first target final Target target = targets.get(0); diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/AbstractApiRestDocumentation.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/AbstractApiRestDocumentation.java index b370a7dee..ce20c076e 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/AbstractApiRestDocumentation.java +++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/documentation/AbstractApiRestDocumentation.java @@ -196,7 +196,7 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati // start the rollout and handle it rolloutManagement.start(rollout.getId()); - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); updatedTargets = Collections.singletonList(savedTarget); diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/RolloutResourceDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/RolloutResourceDocumentationTest.java index d5096edce..2fe15c6f9 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/RolloutResourceDocumentationTest.java +++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/RolloutResourceDocumentationTest.java @@ -416,7 +416,7 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati rolloutManagement.start(rollout.getId()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); mockMvc.perform(post(MgmtRestConstants.ROLLOUT_V1_REQUEST_MAPPING + "/{rolloutId}/pause", rollout.getId()) .accept(MediaTypes.HAL_JSON_VALUE)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) @@ -431,7 +431,7 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati rolloutManagement.start(rollout.getId()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); rolloutManagement.pauseRollout(rollout.getId()); mockMvc.perform(post(MgmtRestConstants.ROLLOUT_V1_REQUEST_MAPPING + "/{rolloutId}/resume", rollout.getId()) @@ -471,7 +471,7 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati rolloutManagement.start(rollout.getId()); // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); mockMvc.perform( post(MgmtRestConstants.ROLLOUT_V1_REQUEST_MAPPING + "/{rolloutId}/triggerNextGroup", rollout.getId()) @@ -692,7 +692,7 @@ public class RolloutResourceDocumentationTest extends AbstractApiRestDocumentati .withDefaults().successCondition(RolloutGroupSuccessCondition.THRESHOLD, "10").build()); // Run here, because Scheduler is disabled during tests - rolloutManagement.handleRollouts(); + rolloutHandler.handleAll(); return rolloutManagement .update(entityFactory.rollout().update(rollout.getId()).startAt(System.currentTimeMillis() + 1000).description("exampleDescription"));