Introduce Pause Success Action (#2867)
* Introduce Pause Success Action Signed-off-by: vasilchev <vasil.ilchev@bosch.com> * Instead of overriding SuccessAction, trigger next group from resume rollout Fix Rollout Mgmt Resource to accept new Pause Action Signed-off-by: vasilchev <vasil.ilchev@bosch.com> * Review findings Signed-off-by: vasilchev <vasil.ilchev@bosch.com> * Remove unused import --------- Signed-off-by: vasilchev <vasil.ilchev@bosch.com>
This commit is contained in:
@@ -38,6 +38,7 @@ public class MgmtRolloutSuccessAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum SuccessAction {
|
public enum SuccessAction {
|
||||||
NEXTGROUP
|
NEXTGROUP,
|
||||||
|
PAUSE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -294,17 +294,17 @@ public final class MgmtRolloutMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static RolloutGroupSuccessAction map(final SuccessAction action) {
|
private static RolloutGroupSuccessAction map(final SuccessAction action) {
|
||||||
if (SuccessAction.NEXTGROUP == action) {
|
return switch (action) {
|
||||||
return RolloutGroupSuccessAction.NEXTGROUP;
|
case NEXTGROUP -> RolloutGroupSuccessAction.NEXTGROUP;
|
||||||
}
|
case PAUSE -> RolloutGroupSuccessAction.PAUSE;
|
||||||
throw new IllegalArgumentException("Success Action " + action + NOT_SUPPORTED);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SuccessAction map(final RolloutGroupSuccessAction successAction) {
|
private static SuccessAction map(final RolloutGroupSuccessAction successAction) {
|
||||||
if (RolloutGroupSuccessAction.NEXTGROUP == successAction) {
|
return switch (successAction) {
|
||||||
return SuccessAction.NEXTGROUP;
|
case NEXTGROUP -> SuccessAction.NEXTGROUP;
|
||||||
}
|
case PAUSE -> SuccessAction.PAUSE;
|
||||||
throw new IllegalArgumentException("Rollout group success action " + successAction + NOT_SUPPORTED);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ErrorAction map(final RolloutGroupErrorAction errorAction) {
|
private static ErrorAction map(final RolloutGroupErrorAction errorAction) {
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ public interface RolloutGroup extends NamedEntity {
|
|||||||
* is hit.
|
* is hit.
|
||||||
*/
|
*/
|
||||||
enum RolloutGroupSuccessAction {
|
enum RolloutGroupSuccessAction {
|
||||||
NEXTGROUP
|
NEXTGROUP,
|
||||||
|
PAUSE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,7 +70,8 @@ import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
|
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
|
||||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
|
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
|
||||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
|
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
|
||||||
import org.eclipse.hawkbit.repository.jpa.rollout.condition.PauseRolloutGroupAction;
|
import org.eclipse.hawkbit.repository.jpa.rollout.condition.PauseRolloutGroupErrorAction;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.rollout.condition.PauseRolloutGroupSuccessAction;
|
||||||
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupActionEvaluator;
|
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.RolloutGroupConditionEvaluator;
|
||||||
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupEvaluationManager;
|
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupEvaluationManager;
|
||||||
@@ -213,9 +214,9 @@ public class JpaRepositoryConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
PauseRolloutGroupAction pauseRolloutGroupAction(
|
PauseRolloutGroupErrorAction pauseRolloutGroupErrorAction(
|
||||||
final RolloutManagement rolloutManagement, final RolloutGroupRepository rolloutGroupRepository) {
|
final RolloutManagement rolloutManagement, final RolloutGroupRepository rolloutGroupRepository) {
|
||||||
return new PauseRolloutGroupAction(rolloutManagement, rolloutGroupRepository);
|
return new PauseRolloutGroupErrorAction(rolloutManagement, rolloutGroupRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -225,6 +226,13 @@ public class JpaRepositoryConfiguration {
|
|||||||
return new StartNextGroupRolloutGroupSuccessAction(rolloutGroupRepository, deploymentManagement);
|
return new StartNextGroupRolloutGroupSuccessAction(rolloutGroupRepository, deploymentManagement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
PauseRolloutGroupSuccessAction pauseRolloutGroupSuccessAction(final RolloutManagement rolloutManagement,
|
||||||
|
final RolloutGroupRepository rolloutGroupRepository) {
|
||||||
|
return new PauseRolloutGroupSuccessAction(rolloutManagement, rolloutGroupRepository);
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
ThresholdRolloutGroupErrorCondition thresholdRolloutGroupErrorCondition(final ActionRepository actionRepository) {
|
ThresholdRolloutGroupErrorCondition thresholdRolloutGroupErrorCondition(final ActionRepository actionRepository) {
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository;
|
|||||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
|
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
|
||||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
|
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
|
||||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
|
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
|
||||||
|
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.StartNextGroupRolloutGroupSuccessAction;
|
||||||
import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
|
import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
|
||||||
import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification;
|
import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification;
|
||||||
@@ -86,8 +87,10 @@ import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
|
|||||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
||||||
import org.eclipse.hawkbit.repository.qfields.RolloutFields;
|
import org.eclipse.hawkbit.repository.qfields.RolloutFields;
|
||||||
import org.eclipse.hawkbit.utils.ObjectCopyUtil;
|
import org.eclipse.hawkbit.utils.ObjectCopyUtil;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.dao.ConcurrencyFailureException;
|
import org.springframework.dao.ConcurrencyFailureException;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
@@ -118,6 +121,9 @@ public class JpaRolloutManagement implements RolloutManagement {
|
|||||||
RolloutStatus.CREATING, RolloutStatus.READY, RolloutStatus.WAITING_FOR_APPROVAL, RolloutStatus.STARTING, RolloutStatus.RUNNING,
|
RolloutStatus.CREATING, RolloutStatus.READY, RolloutStatus.WAITING_FOR_APPROVAL, RolloutStatus.STARTING, RolloutStatus.RUNNING,
|
||||||
RolloutStatus.PAUSED, RolloutStatus.APPROVAL_DENIED);
|
RolloutStatus.PAUSED, RolloutStatus.APPROVAL_DENIED);
|
||||||
|
|
||||||
|
private static final Comparator<RolloutGroup> ROLLOUT_GROUP_DESC_COMP = Comparator.comparingLong(RolloutGroup::getId).reversed();
|
||||||
|
|
||||||
|
private RolloutGroupEvaluationManager rolloutGroupEvaluationManager;
|
||||||
@Value("${hawkbit.repository.jpa.management.rollout.max.actions.per.transaction:5000}")
|
@Value("${hawkbit.repository.jpa.management.rollout.max.actions.per.transaction:5000}")
|
||||||
private int maxActions;
|
private int maxActions;
|
||||||
|
|
||||||
@@ -164,6 +170,13 @@ public class JpaRolloutManagement implements RolloutManagement {
|
|||||||
quotaManagement, this::isMultiAssignmentsEnabled, this::isConfirmationFlowEnabled, repositoryProperties, null);
|
quotaManagement, this::isMultiAssignmentsEnabled, this::isConfirmationFlowEnabled, repositoryProperties, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Lazy
|
||||||
|
private void setRolloutGroupEvaluationManager(
|
||||||
|
final RolloutGroupEvaluationManager rolloutGroupEvaluationManager) {
|
||||||
|
this.rolloutGroupEvaluationManager = rolloutGroupEvaluationManager;
|
||||||
|
}
|
||||||
|
|
||||||
public static String createRolloutLockKey(final String tenant) {
|
public static String createRolloutLockKey(final String tenant) {
|
||||||
return tenant + "-rollout";
|
return tenant + "-rollout";
|
||||||
}
|
}
|
||||||
@@ -348,10 +361,34 @@ public class JpaRolloutManagement implements RolloutManagement {
|
|||||||
throw new RolloutIllegalStateException("Rollout can only be resumed in state paused but current state is " +
|
throw new RolloutIllegalStateException("Rollout can only be resumed in state paused but current state is " +
|
||||||
rollout.getStatus().name().toLowerCase());
|
rollout.getStatus().name().toLowerCase());
|
||||||
}
|
}
|
||||||
|
final List<RolloutGroup> allStartedGroups = rollout.getRolloutGroups().stream()
|
||||||
|
.filter(g -> RolloutGroupStatus.SCHEDULED != g.getStatus()).toList();
|
||||||
|
if (!allStartedGroups.isEmpty()) {
|
||||||
|
final RolloutGroup lastStartedGroup = allStartedGroups.get(allStartedGroups.size() - 1);
|
||||||
|
if (shouldStartNextGroupOnResume(rollout, lastStartedGroup)) {
|
||||||
|
startNextRolloutGroupAction.exec(rollout, lastStartedGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
rollout.setStatus(RolloutStatus.RUNNING);
|
rollout.setStatus(RolloutStatus.RUNNING);
|
||||||
rolloutRepository.save(rollout);
|
rolloutRepository.save(rollout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if on resume of a paused rollout the next group shall be started directly.
|
||||||
|
* Cases where we need to manually start the next group:
|
||||||
|
* - last running group is in error state and there is still some old group in running state, only running groups would be evaluated which would leave Rollout in running state but no trigger new group
|
||||||
|
* - last running group has success action to PAUSE and the success condition is fulfilled
|
||||||
|
* @param rollout
|
||||||
|
* @param lastStartedGroup
|
||||||
|
* @return true if next group shall be started directly on resume, false otherwise
|
||||||
|
*/
|
||||||
|
private boolean shouldStartNextGroupOnResume(final JpaRollout rollout, final RolloutGroup lastStartedGroup) {
|
||||||
|
return lastStartedGroup.getStatus().equals(RolloutGroupStatus.ERROR) ||
|
||||||
|
(lastStartedGroup.getSuccessAction() == RolloutGroup.RolloutGroupSuccessAction.PAUSE &&
|
||||||
|
rolloutGroupEvaluationManager.getSuccessConditionEvaluator(lastStartedGroup.getSuccessCondition())
|
||||||
|
.eval(rollout, lastStartedGroup, lastStartedGroup.getSuccessConditionExp()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||||
@@ -482,13 +519,15 @@ public class JpaRolloutManagement implements RolloutManagement {
|
|||||||
throw new RolloutIllegalStateException("Rollout does not have any groups left to be triggered");
|
throw new RolloutIllegalStateException("Rollout does not have any groups left to be triggered");
|
||||||
}
|
}
|
||||||
|
|
||||||
final RolloutGroup latestRunning = groups.stream()
|
final List<JpaRolloutGroup> startedRolloutGroups = rollout.getRolloutGroups().stream()
|
||||||
.sorted(Comparator.comparingLong(RolloutGroup::getId).reversed())
|
.filter(group -> group.getStatus() != RolloutGroupStatus.SCHEDULED)
|
||||||
.filter(g -> RolloutGroupStatus.RUNNING.equals(g.getStatus()))
|
.sorted(ROLLOUT_GROUP_DESC_COMP)
|
||||||
.findFirst()
|
.map(JpaRolloutGroup.class::cast)
|
||||||
.orElseThrow(() -> new RolloutIllegalStateException("No group is running"));
|
.toList();
|
||||||
|
if (startedRolloutGroups.isEmpty()) {
|
||||||
startNextRolloutGroupAction.exec(rollout, latestRunning);
|
throw new RolloutIllegalStateException("Cannot find any started rollout group to trigger next from");
|
||||||
|
}
|
||||||
|
startNextRolloutGroupAction.exec(rollout, startedRolloutGroups.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
* Copyright (c) 2025 Contributors to the Eclipse Foundation
|
||||||
*
|
*
|
||||||
* This program and the accompanying materials are made
|
* This program and the accompanying materials are made
|
||||||
* available under the terms of the Eclipse Public License 2.0
|
* available under the terms of the Eclipse Public License 2.0
|
||||||
@@ -12,38 +12,27 @@ package org.eclipse.hawkbit.repository.jpa.rollout.condition;
|
|||||||
import static org.eclipse.hawkbit.context.AccessContext.asSystem;
|
import static org.eclipse.hawkbit.context.AccessContext.asSystem;
|
||||||
|
|
||||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
|
||||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
|
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error action evaluator which pauses the whole {@link Rollout} and sets the
|
* Abstract class for pausing a rollout group.
|
||||||
* current {@link RolloutGroup} to error.
|
*
|
||||||
|
* @param <T> the type of the action
|
||||||
*/
|
*/
|
||||||
public class PauseRolloutGroupAction implements RolloutGroupActionEvaluator<RolloutGroup.RolloutGroupErrorAction> {
|
public abstract class AbstractPauseRolloutGroupAction<T extends Enum<T>> implements RolloutGroupActionEvaluator<T> {
|
||||||
|
|
||||||
private final RolloutManagement rolloutManagement;
|
protected final RolloutManagement rolloutManagement;
|
||||||
private final RolloutGroupRepository rolloutGroupRepository;
|
protected final RolloutGroupRepository rolloutGroupRepository;
|
||||||
|
|
||||||
public PauseRolloutGroupAction(final RolloutManagement rolloutManagement,
|
protected AbstractPauseRolloutGroupAction(final RolloutManagement rolloutManagement,
|
||||||
final RolloutGroupRepository rolloutGroupRepository) {
|
final RolloutGroupRepository rolloutGroupRepository) {
|
||||||
this.rolloutManagement = rolloutManagement;
|
this.rolloutManagement = rolloutManagement;
|
||||||
this.rolloutGroupRepository = rolloutGroupRepository;
|
this.rolloutGroupRepository = rolloutGroupRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void exec(final Rollout rollout, final RolloutGroup rolloutGroup) {
|
||||||
public RolloutGroup.RolloutGroupErrorAction getAction() {
|
|
||||||
return RolloutGroup.RolloutGroupErrorAction.PAUSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exec(final Rollout rollout, final RolloutGroup rolloutG) {
|
|
||||||
final JpaRolloutGroup rolloutGroup = (JpaRolloutGroup) rolloutG;
|
|
||||||
|
|
||||||
rolloutGroup.setStatus(RolloutGroupStatus.ERROR);
|
|
||||||
rolloutGroupRepository.save(rolloutGroup);
|
|
||||||
// Refresh latest rollout state in order to avoid cases when
|
// Refresh latest rollout state in order to avoid cases when
|
||||||
// previous group have matched error condition and paused the rollout
|
// previous group have matched error condition and paused the rollout
|
||||||
// and this one tries to pause the rollout too but throws an exception
|
// and this one tries to pause the rollout too but throws an exception
|
||||||
@@ -56,3 +45,4 @@ public class PauseRolloutGroupAction implements RolloutGroupActionEvaluator<Roll
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Contributors to the Eclipse Foundation
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made
|
||||||
|
* available under the terms of the Eclipse Public License 2.0
|
||||||
|
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.repository.jpa.rollout.condition;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
|
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||||
|
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error action evaluator which pauses the whole {@link Rollout} and sets the
|
||||||
|
* current {@link RolloutGroup} to error.
|
||||||
|
*/
|
||||||
|
public class PauseRolloutGroupErrorAction extends AbstractPauseRolloutGroupAction<RolloutGroup.RolloutGroupErrorAction> {
|
||||||
|
|
||||||
|
public PauseRolloutGroupErrorAction(final RolloutManagement rolloutManagement,
|
||||||
|
final RolloutGroupRepository rolloutGroupRepository) {
|
||||||
|
super(rolloutManagement, rolloutGroupRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RolloutGroup.RolloutGroupErrorAction getAction() {
|
||||||
|
return RolloutGroup.RolloutGroupErrorAction.PAUSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exec(final Rollout rollout, final RolloutGroup rolloutG) {
|
||||||
|
// set rollout group status to error
|
||||||
|
final JpaRolloutGroup rolloutGroup = (JpaRolloutGroup) rolloutG;
|
||||||
|
rolloutGroup.setStatus(RolloutGroupStatus.ERROR);
|
||||||
|
rolloutGroupRepository.save(rolloutGroup);
|
||||||
|
// pause the rollout
|
||||||
|
super.exec(rollout, rolloutGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Contributors to the Eclipse Foundation
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made
|
||||||
|
* available under the terms of the Eclipse Public License 2.0
|
||||||
|
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.repository.jpa.rollout.condition;
|
||||||
|
|
||||||
|
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||||
|
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
|
||||||
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
|
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Success action evaluator which pauses the whole {@link Rollout}.
|
||||||
|
*/
|
||||||
|
public class PauseRolloutGroupSuccessAction
|
||||||
|
extends AbstractPauseRolloutGroupAction<RolloutGroup.RolloutGroupSuccessAction> {
|
||||||
|
|
||||||
|
public PauseRolloutGroupSuccessAction(final RolloutManagement rolloutManagement,
|
||||||
|
final RolloutGroupRepository rolloutGroupRepository) {
|
||||||
|
super(rolloutManagement, rolloutGroupRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RolloutGroup.RolloutGroupSuccessAction getAction() {
|
||||||
|
return RolloutGroup.RolloutGroupSuccessAction.PAUSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exec(final Rollout rollout, final RolloutGroup rolloutGroup) {
|
||||||
|
if (!rolloutGroupRepository
|
||||||
|
.findByParentIdAndStatus(rolloutGroup.getId(), RolloutGroup.RolloutGroupStatus.SCHEDULED).isEmpty()) {
|
||||||
|
// if there are still scheduled child groups, do pause the rollout, otherwise just let it in running state
|
||||||
|
super.exec(rollout, rolloutGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -111,7 +111,6 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
|||||||
*/
|
*/
|
||||||
private static final List<Status> DOWNLOAD_ONLY_ACTION_TERMINATION_STATUSES =
|
private static final List<Status> DOWNLOAD_ONLY_ACTION_TERMINATION_STATUSES =
|
||||||
List.of(Status.ERROR, Status.FINISHED, Status.CANCELED, Status.DOWNLOADED);
|
List.of(Status.ERROR, Status.FINISHED, Status.CANCELED, Status.DOWNLOADED);
|
||||||
private static final Comparator<RolloutGroup> DESC_COMP = Comparator.comparingLong(RolloutGroup::getId).reversed();
|
|
||||||
private static final String TRANSACTION_ASSIGNING_TARGETS_TO_ROLLOUT_GROUP_FAILED = "Transaction assigning Targets to RolloutGroup failed";
|
private static final String TRANSACTION_ASSIGNING_TARGETS_TO_ROLLOUT_GROUP_FAILED = "Transaction assigning Targets to RolloutGroup failed";
|
||||||
|
|
||||||
private final ActionRepository actionRepository;
|
private final ActionRepository actionRepository;
|
||||||
@@ -353,11 +352,10 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
|||||||
.filter(group -> group.getStatus() == RolloutGroupStatus.RUNNING)
|
.filter(group -> group.getStatus() == RolloutGroupStatus.RUNNING)
|
||||||
.map(JpaRolloutGroup.class::cast)
|
.map(JpaRolloutGroup.class::cast)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (runningGroups.isEmpty()) {
|
if (runningGroups.isEmpty()) {
|
||||||
// no running rollouts, probably there was an error somewhere at the latest group. And the latest group has
|
// no running rollouts, probably there was an error somewhere at the latest group. And the latest group has
|
||||||
// been switched from running into error state. So we need to find the latest group which
|
// been switched from running into error state. So we need to find the latest group which
|
||||||
executeLatestRolloutGroup(rollout);
|
asSystem(() -> rolloutManagement.triggerNextGroup(rollout.getId()));
|
||||||
} else {
|
} else {
|
||||||
log.debug("Rollout {} has {} running groups", rollout.getId(), runningGroups.size());
|
log.debug("Rollout {} has {} running groups", rollout.getId(), runningGroups.size());
|
||||||
executeRunningGroups(rollout, runningGroups, rollout.getRolloutGroups().get(rollout.getRolloutGroups().size() - 1));
|
executeRunningGroups(rollout, runningGroups, rollout.getRolloutGroups().get(rollout.getRolloutGroups().size() - 1));
|
||||||
@@ -410,18 +408,6 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
|||||||
return groupsActiveLeft == 0;
|
return groupsActiveLeft == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void executeLatestRolloutGroup(final JpaRollout rollout) {
|
|
||||||
final List<JpaRolloutGroup> latestRolloutGroup = rollout.getRolloutGroups().stream()
|
|
||||||
.filter(group -> group.getStatus() != RolloutGroupStatus.SCHEDULED)
|
|
||||||
.sorted(DESC_COMP)
|
|
||||||
.map(JpaRolloutGroup.class::cast)
|
|
||||||
.toList();
|
|
||||||
if (latestRolloutGroup.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
executeRolloutGroupSuccessAction(rollout, latestRolloutGroup.get(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// fakes getTotalTargets count to match expected for the last dynamic group
|
// fakes getTotalTargets count to match expected for the last dynamic group
|
||||||
// so the evaluation to use total targets to properly
|
// so the evaluation to use total targets to properly
|
||||||
private RolloutGroup evalProxy(final RolloutGroup group) {
|
private RolloutGroup evalProxy(final RolloutGroup group) {
|
||||||
@@ -530,7 +516,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
|||||||
.eval(rollout, evalProxy, rolloutGroup.getSuccessConditionExp());
|
.eval(rollout, evalProxy, rolloutGroup.getSuccessConditionExp());
|
||||||
if (isFinished) {
|
if (isFinished) {
|
||||||
log.debug("Rollout group {} is finished, starting next group", rolloutGroup);
|
log.debug("Rollout group {} is finished, starting next group", rolloutGroup);
|
||||||
executeRolloutGroupSuccessAction(rollout, rolloutGroup);
|
evaluationManager.getSuccessActionEvaluator(rolloutGroup.getSuccessAction()).exec(rollout, rolloutGroup);
|
||||||
} else {
|
} else {
|
||||||
log.debug("Rollout group {} is still running", rolloutGroup);
|
log.debug("Rollout group {} is still running", rolloutGroup);
|
||||||
}
|
}
|
||||||
@@ -539,10 +525,6 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void executeRolloutGroupSuccessAction(final Rollout rollout, final RolloutGroup rolloutGroup) {
|
|
||||||
evaluationManager.getSuccessActionEvaluator(rolloutGroup.getSuccessAction()).exec(rollout, rolloutGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startFirstRolloutGroup(final JpaRollout rollout) {
|
private void startFirstRolloutGroup(final JpaRollout rollout) {
|
||||||
log.debug("startFirstRolloutGroup called for rollout {}", rollout.getId());
|
log.debug("startFirstRolloutGroup called for rollout {}", rollout.getId());
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import org.eclipse.hawkbit.repository.model.Rollout;
|
|||||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||||
|
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessAction;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
@@ -52,7 +53,21 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
*/
|
*/
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Test
|
@Test
|
||||||
void rolloutFlow() {
|
void rolloutDefaultFlow() {
|
||||||
|
rolloutFlow(RolloutGroupSuccessAction.NEXTGROUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a simple rollout flow
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
@Test
|
||||||
|
void rolloutPauseFlow() {
|
||||||
|
rolloutFlow(RolloutGroupSuccessAction.PAUSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void rolloutFlow(final RolloutGroupSuccessAction successAction) throws Exception {
|
||||||
final String rolloutName = "rollout-std";
|
final String rolloutName = "rollout-std";
|
||||||
final int amountGroups = 5; // static only
|
final int amountGroups = 5; // static only
|
||||||
final String targetPrefix = "controller-rollout-std-";
|
final String targetPrefix = "controller-rollout-std-";
|
||||||
@@ -62,7 +77,7 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
final Rollout rollout = callAs(
|
final Rollout rollout = callAs(
|
||||||
withUser("rolloutFlowUser", "READ_DISTRIBUTION_SET", "READ_TARGET", "READ_ROLLOUT", "CREATE_ROLLOUT"),
|
withUser("rolloutFlowUser", "READ_DISTRIBUTION_SET", "READ_TARGET", "READ_ROLLOUT", "CREATE_ROLLOUT"),
|
||||||
() -> testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
() -> testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
||||||
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30", false, false));
|
"controllerid==" + targetPrefix + "*", distributionSet, "60", successAction,"30", false, false));
|
||||||
final List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(
|
final List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(
|
||||||
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))).getContent();
|
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))).getContent();
|
||||||
|
|
||||||
@@ -78,7 +93,9 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
assertGroup(groups.get(i), false, i == 0 ? RolloutGroupStatus.RUNNING : RolloutGroupStatus.SCHEDULED, 3);
|
assertGroup(groups.get(i), false, i == 0 ? RolloutGroupStatus.RUNNING : RolloutGroupStatus.SCHEDULED, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
executeStaticWithoutOneTargetFromTheLastGroupAndHandleAll(groups, rollout, amountGroups);
|
executeStaticWithoutOneTargetFromTheLastGroupAndHandleAll(groups, rollout, amountGroups, successAction);
|
||||||
|
|
||||||
|
assertRollout(rollout, false, RolloutStatus.RUNNING, amountGroups, amountGroups * 3);
|
||||||
|
|
||||||
rolloutManagement.pauseRollout(rollout.getId());
|
rolloutManagement.pauseRollout(rollout.getId());
|
||||||
rolloutHandler.handleAll();
|
rolloutHandler.handleAll();
|
||||||
@@ -92,11 +109,24 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies a simple dynamic rollout flow
|
* Verifies a simple dynamic rollout flow with default {@link RolloutGroupSuccessAction#NEXTGROUP} success action
|
||||||
*/
|
*/
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Test
|
@Test
|
||||||
void dynamicRolloutFlow() {
|
void dynamicRolloutDefaultFlow() {
|
||||||
|
dynamicRolloutFlow(RolloutGroupSuccessAction.NEXTGROUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a simple dynamic rollout flow with {@link RolloutGroupSuccessAction#PAUSE} success action
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
@Test
|
||||||
|
void dynamicRolloutPauseFlow() {
|
||||||
|
dynamicRolloutFlow(RolloutGroupSuccessAction.PAUSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dynamicRolloutFlow(final RolloutGroup.RolloutGroupSuccessAction successAction) throws Exception {
|
||||||
final String rolloutName = "dynamic-rollout-std";
|
final String rolloutName = "dynamic-rollout-std";
|
||||||
final int amountGroups = 2; // static only
|
final int amountGroups = 2; // static only
|
||||||
final String targetPrefix = "controller-dynamic-rollout-std-";
|
final String targetPrefix = "controller-dynamic-rollout-std-";
|
||||||
@@ -106,7 +136,7 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
final Rollout rollout = callAs(
|
final Rollout rollout = callAs(
|
||||||
withUser("dynamicRolloutFlow", "READ_DISTRIBUTION_SET", "READ_TARGET", "READ_ROLLOUT", "CREATE_ROLLOUT"),
|
withUser("dynamicRolloutFlow", "READ_DISTRIBUTION_SET", "READ_TARGET", "READ_ROLLOUT", "CREATE_ROLLOUT"),
|
||||||
() -> testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
() -> testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
||||||
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30", false, true));
|
"controllerid==" + targetPrefix + "*", distributionSet, "60", successAction,"30", false, true));
|
||||||
|
|
||||||
// rollout is READY
|
// rollout is READY
|
||||||
assertRollout(rollout, true, RolloutStatus.READY, amountGroups + 1, amountGroups * 3);
|
assertRollout(rollout, true, RolloutStatus.READY, amountGroups + 1, amountGroups * 3);
|
||||||
@@ -132,7 +162,7 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
assertGroup(dynamic1, true, RolloutGroupStatus.SCHEDULED, 0);
|
assertGroup(dynamic1, true, RolloutGroupStatus.SCHEDULED, 0);
|
||||||
|
|
||||||
executeStaticWithoutOneTargetFromTheLastGroupAndHandleAll(groups, rollout, amountGroups);
|
executeStaticWithoutOneTargetFromTheLastGroupAndHandleAll(groups, rollout, amountGroups, successAction);
|
||||||
|
|
||||||
// partially fill the first dynamic (it is running and now create actions for 2 targets)
|
// partially fill the first dynamic (it is running and now create actions for 2 targets)
|
||||||
rolloutHandler.handleAll();
|
rolloutHandler.handleAll();
|
||||||
@@ -166,7 +196,13 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
.forEach(this::finishAction);
|
.forEach(this::finishAction);
|
||||||
executeWithoutOneTargetFromAGroup(dynamic1, rollout, 3);
|
executeWithoutOneTargetFromAGroup(dynamic1, rollout, 3);
|
||||||
assertAndGetRunning(rollout, 1); // remains on in the first dynamic
|
assertAndGetRunning(rollout, 1); // remains on in the first dynamic
|
||||||
|
if (successAction == RolloutGroupSuccessAction.PAUSE) {
|
||||||
|
// let success pause action run
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
// external resume rollout
|
||||||
|
rolloutManagement.resumeRollout(rollout.getId());
|
||||||
|
}
|
||||||
|
// start next group
|
||||||
rolloutHandler.handleAll();
|
rolloutHandler.handleAll();
|
||||||
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 2, amountGroups * 3 + 4);
|
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 2, amountGroups * 3 + 4);
|
||||||
assertGroup(groups.get(amountGroups - 1), false, RolloutGroupStatus.FINISHED, 3);
|
assertGroup(groups.get(amountGroups - 1), false, RolloutGroupStatus.FINISHED, 3);
|
||||||
@@ -208,12 +244,26 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
assertThat(refresh(dynamic2).getStatus()).isEqualTo(RolloutGroupStatus.FINISHED);
|
assertThat(refresh(dynamic2).getStatus()).isEqualTo(RolloutGroupStatus.FINISHED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies a simple dynamic rollout flow with a dynamic group template
|
* Verifies a simple dynamic rollout flow with a dynamic group template with default {@link RolloutGroupSuccessAction#NEXTGROUP} success action
|
||||||
*/
|
*/
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Test
|
@Test
|
||||||
void dynamicRolloutTemplateFlow() {
|
void dynamicDefaultRolloutTemplateFlow() {
|
||||||
|
dynamicRolloutTemplateFlow(RolloutGroup.RolloutGroupSuccessAction.NEXTGROUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a simple dynamic rollout flow with a dynamic group template with {@link RolloutGroupSuccessAction#PAUSE} success action
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
@Test
|
||||||
|
void dynamicPauseRolloutTemplateFlow() {
|
||||||
|
dynamicRolloutTemplateFlow(RolloutGroup.RolloutGroupSuccessAction.PAUSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dynamicRolloutTemplateFlow(final RolloutGroup.RolloutGroupSuccessAction successAction) throws Exception {
|
||||||
final String rolloutName = "dynamic-template-rollout-std";
|
final String rolloutName = "dynamic-template-rollout-std";
|
||||||
final int amountGroups = 3; // static only
|
final int amountGroups = 3; // static only
|
||||||
final String targetPrefix = "controller-template-dynamic-rollout-std-";
|
final String targetPrefix = "controller-template-dynamic-rollout-std-";
|
||||||
@@ -224,7 +274,7 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
final Rollout rollout = callAs(
|
final Rollout rollout = callAs(
|
||||||
withUser("dynamicRolloutTemplateFlow", "READ_DISTRIBUTION_SET", "READ_TARGET", "READ_ROLLOUT", "CREATE_ROLLOUT"),
|
withUser("dynamicRolloutTemplateFlow", "READ_DISTRIBUTION_SET", "READ_TARGET", "READ_ROLLOUT", "CREATE_ROLLOUT"),
|
||||||
() -> testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
() -> testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
||||||
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30",
|
"controllerid==" + targetPrefix + "*", distributionSet, "60", successAction, "30",
|
||||||
Action.ActionType.FORCED, 1000, false, true,
|
Action.ActionType.FORCED, 1000, false, true,
|
||||||
RolloutManagement.DynamicRolloutGroupTemplate.builder().nameSuffix("-dyn").targetCount(6).build()));
|
RolloutManagement.DynamicRolloutGroupTemplate.builder().nameSuffix("-dyn").targetCount(6).build()));
|
||||||
|
|
||||||
@@ -252,7 +302,7 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
assertGroup(dynamic1, true, RolloutGroupStatus.SCHEDULED, 0);
|
assertGroup(dynamic1, true, RolloutGroupStatus.SCHEDULED, 0);
|
||||||
|
|
||||||
executeStaticWithoutOneTargetFromTheLastGroupAndHandleAll(groups, rollout, amountGroups);
|
executeStaticWithoutOneTargetFromTheLastGroupAndHandleAll(groups, rollout, amountGroups, successAction);
|
||||||
|
|
||||||
// partially fill the first dynamic (it is running and now create actions for 4 targets)
|
// partially fill the first dynamic (it is running and now create actions for 4 targets)
|
||||||
rolloutHandler.handleAll();
|
rolloutHandler.handleAll();
|
||||||
@@ -287,6 +337,13 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
executeWithoutOneTargetFromAGroup(dynamic1, rollout, 6);
|
executeWithoutOneTargetFromAGroup(dynamic1, rollout, 6);
|
||||||
assertAndGetRunning(rollout, 1); // remains on in the first dynamic
|
assertAndGetRunning(rollout, 1); // remains on in the first dynamic
|
||||||
|
|
||||||
|
if (successAction == RolloutGroupSuccessAction.PAUSE) {
|
||||||
|
// let success pause action run
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
// external resume rollout
|
||||||
|
rolloutManagement.resumeRollout(rollout.getId());
|
||||||
|
}
|
||||||
|
// start next group
|
||||||
rolloutHandler.handleAll();
|
rolloutHandler.handleAll();
|
||||||
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 2, amountGroups * 3 + 8);
|
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 2, amountGroups * 3 + 8);
|
||||||
assertGroup(groups.get(amountGroups - 1), false, RolloutGroupStatus.FINISHED, 3);
|
assertGroup(groups.get(amountGroups - 1), false, RolloutGroupStatus.FINISHED, 3);
|
||||||
@@ -310,11 +367,24 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies a simple pure (no static groups) dynamic rollout flow with a dynamic group template
|
* Verifies a simple pure (no static groups) dynamic rollout flow with a dynamic group template with default {@link RolloutGroupSuccessAction#NEXTGROUP} success action
|
||||||
*/
|
*/
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Test
|
@Test
|
||||||
void dynamicRolloutPureFlow() {
|
void dynamicDefaultRolloutPureFlow() {
|
||||||
|
dynamicRolloutPureFlow(RolloutGroup.RolloutGroupSuccessAction.NEXTGROUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a simple pure (no static groups) dynamic rollout flow with a dynamic group template with {@link RolloutGroupSuccessAction#PAUSE} success action
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
@Test
|
||||||
|
void dynamicPauseRolloutPureFlow() {
|
||||||
|
dynamicRolloutPureFlow(RolloutGroup.RolloutGroupSuccessAction.PAUSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dynamicRolloutPureFlow(final RolloutGroup.RolloutGroupSuccessAction successAction) throws Exception {
|
||||||
final String rolloutName = "pure-dynamic-rollout-std";
|
final String rolloutName = "pure-dynamic-rollout-std";
|
||||||
final String targetPrefix = "controller-pure-dynamic-rollout-std-";
|
final String targetPrefix = "controller-pure-dynamic-rollout-std-";
|
||||||
final DistributionSet distributionSet = testdataFactory.createDistributionSetLocked("dsFor" + rolloutName);
|
final DistributionSet distributionSet = testdataFactory.createDistributionSetLocked("dsFor" + rolloutName);
|
||||||
@@ -322,7 +392,7 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
final Rollout rollout = callAs(
|
final Rollout rollout = callAs(
|
||||||
withUser("dynamicRolloutPureFlow", "READ_DISTRIBUTION_SET", "READ_TARGET", "READ_ROLLOUT", "CREATE_ROLLOUT"),
|
withUser("dynamicRolloutPureFlow", "READ_DISTRIBUTION_SET", "READ_TARGET", "READ_ROLLOUT", "CREATE_ROLLOUT"),
|
||||||
() -> testdataFactory.createRolloutByVariables(rolloutName, rolloutName, 0,
|
() -> testdataFactory.createRolloutByVariables(rolloutName, rolloutName, 0,
|
||||||
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30",
|
"controllerid==" + targetPrefix + "*", distributionSet, "60", successAction,"30",
|
||||||
Action.ActionType.FORCED, 1000, false, true,
|
Action.ActionType.FORCED, 1000, false, true,
|
||||||
RolloutManagement.DynamicRolloutGroupTemplate.builder().nameSuffix("-dyn").targetCount(6).build()));
|
RolloutManagement.DynamicRolloutGroupTemplate.builder().nameSuffix("-dyn").targetCount(6).build()));
|
||||||
|
|
||||||
@@ -373,6 +443,13 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
executeWithoutOneTargetFromAGroup(dynamic1, rollout, 6);
|
executeWithoutOneTargetFromAGroup(dynamic1, rollout, 6);
|
||||||
assertAndGetRunning(rollout, 1); // remains on in the first dynamic
|
assertAndGetRunning(rollout, 1); // remains on in the first dynamic
|
||||||
|
|
||||||
|
if (successAction == RolloutGroupSuccessAction.PAUSE) {
|
||||||
|
// let success pause action run
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
// external resume rollout
|
||||||
|
rolloutManagement.resumeRollout(rollout.getId());
|
||||||
|
}
|
||||||
|
// start next group
|
||||||
rolloutHandler.handleAll();
|
rolloutHandler.handleAll();
|
||||||
assertRollout(rollout, true, RolloutStatus.RUNNING, 2, 8);
|
assertRollout(rollout, true, RolloutStatus.RUNNING, 2, 8);
|
||||||
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 6);
|
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 6);
|
||||||
@@ -395,11 +472,24 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies a simple rollout flow
|
* Verifies a simple rollout flow with {@link RolloutGroupSuccessAction#NEXTGROUP} success action
|
||||||
*/
|
*/
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@Test
|
@Test
|
||||||
void rollout0ThresholdFlow() {
|
void rolloutDefault0ThresholdFlow() {
|
||||||
|
rollout0ThresholdFlow(RolloutGroupSuccessAction.NEXTGROUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies a simple rollout flow with {@link RolloutGroupSuccessAction#PAUSE} success action
|
||||||
|
*/
|
||||||
|
@SneakyThrows
|
||||||
|
@Test
|
||||||
|
void rolloutPause0ThresholdFlow() {
|
||||||
|
rollout0ThresholdFlow(RolloutGroupSuccessAction.PAUSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rollout0ThresholdFlow(final RolloutGroup.RolloutGroupSuccessAction successAction) throws Exception {
|
||||||
final String rolloutName = "rollout-std-0threshold";
|
final String rolloutName = "rollout-std-0threshold";
|
||||||
final int amountGroups = 5; // static only
|
final int amountGroups = 5; // static only
|
||||||
final String targetPrefix = "controller-rollout-std-0threshold-";
|
final String targetPrefix = "controller-rollout-std-0threshold-";
|
||||||
@@ -409,7 +499,7 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
final Rollout rollout = callAs(
|
final Rollout rollout = callAs(
|
||||||
withUser("rollout0ThresholdFlow", "READ_DISTRIBUTION_SET", "READ_TARGET", "READ_ROLLOUT", "CREATE_ROLLOUT"),
|
withUser("rollout0ThresholdFlow", "READ_DISTRIBUTION_SET", "READ_TARGET", "READ_ROLLOUT", "CREATE_ROLLOUT"),
|
||||||
() -> testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
() -> testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
||||||
"controllerid==" + targetPrefix + "*", distributionSet, "0", "25", false, false));
|
"controllerid==" + targetPrefix + "*", distributionSet, "0", successAction, "25", false, false));
|
||||||
final List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(
|
final List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(
|
||||||
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))).getContent();
|
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))).getContent();
|
||||||
|
|
||||||
@@ -423,6 +513,12 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
for (int i = 0; i < amountGroups; i++) {
|
for (int i = 0; i < amountGroups; i++) {
|
||||||
assertGroup(groups.get(i), false, i < step ? RolloutGroupStatus.RUNNING : RolloutGroupStatus.SCHEDULED, 3);
|
assertGroup(groups.get(i), false, i < step ? RolloutGroupStatus.RUNNING : RolloutGroupStatus.SCHEDULED, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// expect all groups without last to trigger PAUSE action
|
||||||
|
if (step < amountGroups && successAction == RolloutGroupSuccessAction.PAUSE) {
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
rolloutManagement.resumeRollout(rollout.getId());
|
||||||
|
}
|
||||||
// starting the next group
|
// starting the next group
|
||||||
rolloutHandler.handleAll();
|
rolloutHandler.handleAll();
|
||||||
}
|
}
|
||||||
@@ -430,7 +526,7 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
private void executeStaticWithoutOneTargetFromTheLastGroupAndHandleAll(
|
private void executeStaticWithoutOneTargetFromTheLastGroupAndHandleAll(
|
||||||
final List<RolloutGroup> groups,
|
final List<RolloutGroup> groups,
|
||||||
final Rollout rollout, final int amountGroups) {
|
final Rollout rollout, final int amountGroups, final RolloutGroupSuccessAction successAction) {
|
||||||
// create dynamic group if needed
|
// create dynamic group if needed
|
||||||
rolloutHandler.handleAll();
|
rolloutHandler.handleAll();
|
||||||
// execute groups (without on of the last)
|
// execute groups (without on of the last)
|
||||||
@@ -455,6 +551,10 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
|||||||
.forEach(this::finishAction);
|
.forEach(this::finishAction);
|
||||||
assertAndGetRunning(rollout, i + 1 == amountGroups ? 1 : 0);
|
assertAndGetRunning(rollout, i + 1 == amountGroups ? 1 : 0);
|
||||||
rolloutHandler.handleAll();
|
rolloutHandler.handleAll();
|
||||||
|
if (i + 1 < groups.size() && successAction == RolloutGroupSuccessAction.PAUSE) {
|
||||||
|
rolloutManagement.resumeRollout(rollout.getId());
|
||||||
|
rolloutHandler.handleAll();
|
||||||
|
}
|
||||||
final RolloutGroupStatus expectedStatus =
|
final RolloutGroupStatus expectedStatus =
|
||||||
i + 1 == amountGroups ? RolloutGroupStatus.RUNNING : RolloutGroupStatus.FINISHED;
|
i + 1 == amountGroups ? RolloutGroupStatus.RUNNING : RolloutGroupStatus.FINISHED;
|
||||||
assertThat(refresh(groups.get(i)).getStatus())
|
assertThat(refresh(groups.get(i)).getStatus())
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
testdataFactory.createTargets(targetPrefix, 0, amountGroups * 2);
|
testdataFactory.createTargets(targetPrefix, 0, amountGroups * 2);
|
||||||
final Rollout dynamicRollout = testdataFactory.createRolloutByVariables("dynamic", "static rollout", amountGroups,
|
final Rollout dynamicRollout = testdataFactory.createRolloutByVariables("dynamic", "static rollout", amountGroups,
|
||||||
"controllerid==" + targetPrefix + "*", distributionSet, "0", "30", ActionType.FORCED, 1000, false, true);
|
"controllerid==" + targetPrefix + "*", distributionSet, "0", RolloutGroup.RolloutGroupSuccessAction.NEXTGROUP, "30", ActionType.FORCED, 1000, false, true);
|
||||||
rolloutManagement.start(dynamicRollout.getId());
|
rolloutManagement.start(dynamicRollout.getId());
|
||||||
rolloutHandler.handleAll();
|
rolloutHandler.handleAll();
|
||||||
assertRollout(dynamicRollout, true, RolloutStatus.RUNNING, amountGroups + 1, amountGroups * 2);
|
assertRollout(dynamicRollout, true, RolloutStatus.RUNNING, amountGroups + 1, amountGroups * 2);
|
||||||
@@ -152,7 +152,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
|||||||
|
|
||||||
testdataFactory.createTargets(targetPrefix, amountGroups * 2, amountGroups);
|
testdataFactory.createTargets(targetPrefix, amountGroups * 2, amountGroups);
|
||||||
final Rollout staticRollout = testdataFactory.createRolloutByVariables("static", "static rollout", amountGroups,
|
final Rollout staticRollout = testdataFactory.createRolloutByVariables("static", "static rollout", amountGroups,
|
||||||
"controllerid==" + targetPrefix + "*", distributionSet, "0", "30", ActionType.FORCED, 0, false, false);
|
"controllerid==" + targetPrefix + "*", distributionSet, "0", RolloutGroup.RolloutGroupSuccessAction.NEXTGROUP,"30", ActionType.FORCED, 0, false, false);
|
||||||
rolloutManagement.start(staticRollout.getId());
|
rolloutManagement.start(staticRollout.getId());
|
||||||
rolloutHandler.handleAll();
|
rolloutHandler.handleAll();
|
||||||
assertRollout(staticRollout, false, RolloutStatus.RUNNING, amountGroups, amountGroups * 3);
|
assertRollout(staticRollout, false, RolloutStatus.RUNNING, amountGroups, amountGroups * 3);
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
|||||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||||
import org.eclipse.hawkbit.repository.model.NamedVersionedEntity;
|
import org.eclipse.hawkbit.repository.model.NamedVersionedEntity;
|
||||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||||
|
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorAction;
|
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorAction;
|
||||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorCondition;
|
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorCondition;
|
||||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
|
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
|
||||||
@@ -982,10 +983,10 @@ public class TestdataFactory {
|
|||||||
|
|
||||||
public Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
|
public Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
|
||||||
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
|
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
|
||||||
final String successCondition, final String errorCondition, final boolean confirmationRequired,
|
final String successCondition, final RolloutGroup.RolloutGroupSuccessAction successAction, final String errorCondition, final boolean confirmationRequired,
|
||||||
final boolean dynamic) {
|
final boolean dynamic) {
|
||||||
return createRolloutByVariables(rolloutName, rolloutDescription, groupSize, filterQuery, distributionSet,
|
return createRolloutByVariables(rolloutName, rolloutDescription, groupSize, filterQuery, distributionSet,
|
||||||
successCondition, errorCondition, Action.ActionType.FORCED, null, confirmationRequired, dynamic);
|
successCondition, successAction, errorCondition, Action.ActionType.FORCED, null, confirmationRequired, dynamic);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
|
public Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
|
||||||
@@ -993,7 +994,7 @@ public class TestdataFactory {
|
|||||||
final String successCondition, final String errorCondition, final Action.ActionType actionType,
|
final String successCondition, final String errorCondition, final Action.ActionType actionType,
|
||||||
final Integer weight, final boolean confirmationRequired) {
|
final Integer weight, final boolean confirmationRequired) {
|
||||||
return createRolloutByVariables(rolloutName, rolloutDescription, groupSize, filterQuery, distributionSet,
|
return createRolloutByVariables(rolloutName, rolloutDescription, groupSize, filterQuery, distributionSet,
|
||||||
successCondition, errorCondition, actionType, weight, confirmationRequired, false);
|
successCondition, RolloutGroup.RolloutGroupSuccessAction.NEXTGROUP, errorCondition, actionType, weight, confirmationRequired, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1014,19 +1015,21 @@ public class TestdataFactory {
|
|||||||
*/
|
*/
|
||||||
public Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
|
public Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
|
||||||
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
|
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
|
||||||
final String successCondition, final String errorCondition, final Action.ActionType actionType,
|
final String successCondition, final RolloutGroup.RolloutGroupSuccessAction successAction, final String errorCondition, final Action.ActionType actionType,
|
||||||
final Integer weight, final boolean confirmationRequired, final boolean dynamic) {
|
final Integer weight, final boolean confirmationRequired, final boolean dynamic) {
|
||||||
return createRolloutByVariables(rolloutName, rolloutDescription, groupSize, filterQuery, distributionSet,
|
return createRolloutByVariables(rolloutName, rolloutDescription, groupSize, filterQuery, distributionSet,
|
||||||
successCondition, errorCondition, actionType, weight, confirmationRequired, dynamic, null);
|
successCondition, successAction, errorCondition, actionType, weight, confirmationRequired, dynamic, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
|
public Rollout createRolloutByVariables(final String rolloutName, final String rolloutDescription,
|
||||||
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
|
final int groupSize, final String filterQuery, final DistributionSet distributionSet,
|
||||||
final String successCondition, final String errorCondition, final Action.ActionType actionType,
|
final String successCondition, final RolloutGroup.RolloutGroupSuccessAction successAction, final String errorCondition,
|
||||||
|
final Action.ActionType actionType,
|
||||||
final Integer weight, final boolean confirmationRequired, final boolean dynamic,
|
final Integer weight, final boolean confirmationRequired, final boolean dynamic,
|
||||||
final RolloutManagement.DynamicRolloutGroupTemplate dynamicRolloutGroupTemplate) {
|
final RolloutManagement.DynamicRolloutGroupTemplate dynamicRolloutGroupTemplate) {
|
||||||
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults()
|
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults()
|
||||||
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, successCondition)
|
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, successCondition)
|
||||||
|
.successAction(successAction, "")
|
||||||
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, errorCondition)
|
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, errorCondition)
|
||||||
.errorAction(RolloutGroupErrorAction.PAUSE, null).build();
|
.errorAction(RolloutGroupErrorAction.PAUSE, null).build();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user