Trigger next rollout group - backend and management API implementatio… (#1294)
* Trigger next rollout group - backend and management API implementations. Backend and management API tests. * Trigger next rollout group - Fixed resource documentation test. * Trigger next rollout group - Fixed resource documentation test. * add rest docs * Trigger next rollout group - UI changes. New button for trigger next rollout group in rollout view. * add error test for rest api * Trigger next rollout group - Added test for triggering next group for all rollout states. * add confirm * fix test * replace DB calls * fix translation * fix error message Signed-off-by: Dimitar Shterev <dimitar.shterev@bosch.io> Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io> Co-authored-by: Stefan Klotz <stefan.klotz@bosch.io>
This commit is contained in:
@@ -463,4 +463,20 @@ public interface RolloutManagement {
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_UPDATE)
|
||||
void cancelRolloutsForDistributionSet(DistributionSet set);
|
||||
|
||||
/**
|
||||
* Triggers next group of a rollout for processing even success threshold
|
||||
* isn't met yet. Current running groups will not change their status.
|
||||
*
|
||||
* @param rolloutId
|
||||
* the rollout to be paused.
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* if rollout or group with given ID does not exist
|
||||
* @throws RolloutIllegalStateException
|
||||
* if given rollout is not in {@link RolloutStatus#RUNNING}.
|
||||
*
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_UPDATE)
|
||||
void triggerNextGroup(long rolloutId);
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import static org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate.a
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -48,6 +49,7 @@ import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecuto
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||
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;
|
||||
@@ -128,6 +130,9 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
@Autowired
|
||||
private RolloutStatusCache rolloutStatusCache;
|
||||
|
||||
@Autowired
|
||||
private StartNextGroupRolloutGroupSuccessAction startNextRolloutGroupAction;
|
||||
|
||||
private final TargetManagement targetManagement;
|
||||
private final DistributionSetManagement distributionSetManagement;
|
||||
private final VirtualPropertyReplacer virtualPropertyReplacer;
|
||||
@@ -746,4 +751,30 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
baseFilter, totalTargets, dsTypeId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void triggerNextGroup(final long rolloutId) {
|
||||
final JpaRollout rollout = getRolloutAndThrowExceptionIfNotFound(rolloutId);
|
||||
if (RolloutStatus.RUNNING != rollout.getStatus()) {
|
||||
throw new RolloutIllegalStateException("Rollout is not in running state");
|
||||
}
|
||||
final List<RolloutGroup> groups = rollout.getRolloutGroups();
|
||||
|
||||
final boolean isNextGroupTriggerable = groups.stream()
|
||||
.anyMatch(g -> RolloutGroupStatus.SCHEDULED.equals(g.getStatus()));
|
||||
|
||||
if (!isNextGroupTriggerable) {
|
||||
throw new RolloutIllegalStateException("Rollout does not have any groups left to be triggered");
|
||||
}
|
||||
|
||||
final RolloutGroup latestRunning = groups.stream()
|
||||
.sorted(Comparator.comparingLong(RolloutGroup::getId).reversed())
|
||||
.filter(g -> RolloutGroupStatus.RUNNING.equals(g.getStatus())).findFirst()
|
||||
.orElseThrow(() -> new RolloutIllegalStateException("No group is running"));
|
||||
|
||||
startNextRolloutGroupAction.eval(rollout, latestRunning, latestRunning.getSuccessActionExp());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -173,5 +173,4 @@ public interface RolloutGroupRepository
|
||||
@Modifying
|
||||
@Query("DELETE FROM JpaRolloutGroup g where g.id in :rolloutGroupIds")
|
||||
void deleteByIds(@Param("rolloutGroupIds") List<Long> rolloutGroups);
|
||||
|
||||
}
|
||||
|
||||
@@ -213,6 +213,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
verifyThrownExceptionBy(() -> rolloutManagement.update(entityFactory.rollout().update(NOT_EXIST_IDL)),
|
||||
"Rollout");
|
||||
verifyThrownExceptionBy(() -> rolloutManagement.triggerNextGroup(NOT_EXIST_IDL), "Rollout");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -2070,4 +2071,94 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
() -> rolloutManagement.get(myRolloutId).orElseThrow(NoSuchElementException::new))
|
||||
.getStatus().equals(RolloutStatus.RUNNING));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifying that next group is started on manual trigger next group.")
|
||||
void checkRunningRolloutsManualTriggerNextGroup() {
|
||||
|
||||
final int amountTargetsForRollout = 15;
|
||||
final int amountOtherTargets = 0;
|
||||
final int amountGroups = 3;
|
||||
final String successCondition = "100";
|
||||
final String errorCondition = "80";
|
||||
|
||||
final Rollout createdRollout = createAndStartRollout(amountTargetsForRollout, amountOtherTargets, amountGroups,
|
||||
successCondition, errorCondition);
|
||||
|
||||
// triggers next group
|
||||
rolloutManagement.triggerNextGroup(createdRollout.getId());
|
||||
|
||||
// second group should in running state
|
||||
List<RolloutGroup> rolloutGroups = rolloutGroupManagement
|
||||
.findByRollout(new OffsetBasedPageRequest(0, 10, Sort.by(Direction.ASC, "id")), createdRollout.getId())
|
||||
.getContent();
|
||||
assertThat(rolloutGroups.get(0).getStatus()).isEqualTo(RolloutGroupStatus.RUNNING);
|
||||
assertThat(rolloutGroups.get(1).getStatus()).isEqualTo(RolloutGroupStatus.RUNNING);
|
||||
assertThat(rolloutGroups.get(2).getStatus()).isEqualTo(RolloutGroupStatus.SCHEDULED);
|
||||
|
||||
// triggers next group
|
||||
rolloutManagement.triggerNextGroup(createdRollout.getId());
|
||||
|
||||
// third group should be in running state
|
||||
rolloutGroups = rolloutGroupManagement
|
||||
.findByRollout(new OffsetBasedPageRequest(0, 10, Sort.by(Direction.ASC, "id")), createdRollout.getId())
|
||||
.getContent();
|
||||
assertThat(rolloutGroups.get(0).getStatus()).isEqualTo(RolloutGroupStatus.RUNNING);
|
||||
assertThat(rolloutGroups.get(1).getStatus()).isEqualTo(RolloutGroupStatus.RUNNING);
|
||||
assertThat(rolloutGroups.get(2).getStatus()).isEqualTo(RolloutGroupStatus.RUNNING);
|
||||
|
||||
// finish action of all groups and verify rollout
|
||||
final Slice<JpaAction> runningActionsSlice = actionRepository.findByRolloutIdAndStatus(PAGE,
|
||||
createdRollout.getId(), Status.RUNNING);
|
||||
runningActionsSlice.getContent().forEach(this::finishAction);
|
||||
|
||||
verifyRolloutAndAllGroupsAreFinished(createdRollout);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Trigger next rollout group if rollout is in wrong state")
|
||||
void triggeringNextGroupRolloutWrongState() {
|
||||
|
||||
final int amountTargetsForRollout = 15;
|
||||
final int amountOtherTargets = 0;
|
||||
final int amountGroups = 3;
|
||||
final String successCondition = "100";
|
||||
final String errorCondition = "80";
|
||||
|
||||
final String errorMessage = "Rollout is not in running state";
|
||||
|
||||
final Rollout createdRollout = createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout,
|
||||
amountOtherTargets, amountGroups, successCondition, errorCondition);
|
||||
|
||||
// check CREATING state
|
||||
assertThatExceptionOfType(RolloutIllegalStateException.class)
|
||||
.isThrownBy(() -> rolloutManagement.triggerNextGroup(createdRollout.getId()))
|
||||
.withMessageContaining(errorMessage);
|
||||
|
||||
rolloutManagement.start(createdRollout.getId());
|
||||
// check STARTING state
|
||||
assertThatExceptionOfType(RolloutIllegalStateException.class)
|
||||
.isThrownBy(() -> rolloutManagement.triggerNextGroup(createdRollout.getId()))
|
||||
.withMessageContaining(errorMessage);
|
||||
|
||||
// Run here, because scheduler is disabled during tests
|
||||
rolloutManagement.handleRollouts();
|
||||
final Rollout rollout = reloadRollout(createdRollout);
|
||||
|
||||
rolloutManagement.pauseRollout(rollout.getId());
|
||||
|
||||
// check STOPPED state
|
||||
assertThatExceptionOfType(RolloutIllegalStateException.class)
|
||||
.isThrownBy(() -> rolloutManagement.triggerNextGroup(createdRollout.getId()))
|
||||
.withMessageContaining(errorMessage);
|
||||
|
||||
final Slice<JpaAction> runningActionsSlice = actionRepository.findByRolloutIdAndStatus(PAGE,
|
||||
createdRollout.getId(), Status.RUNNING);
|
||||
runningActionsSlice.getContent().forEach(this::finishAction);
|
||||
|
||||
// check FINISHED state
|
||||
assertThatExceptionOfType(RolloutIllegalStateException.class)
|
||||
.isThrownBy(() -> rolloutManagement.triggerNextGroup(createdRollout.getId()))
|
||||
.withMessageContaining(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user