Rollout retry (#1454)
* Rollout retry mechanism initial commit Signed-off-by: Stanislav Trailov <Stanislav.Trailov@bosch.io> * Remove test target fields for filter query Signed-off-by: Stanislav Trailov <Stanislav.Trailov@bosch.io> * minor refactoring Signed-off-by: Stanislav Trailov <Stanislav.Trailov@bosch.io> * Fixes after review Signed-off-by: Stanislav Trailov <Stanislav.Trailov@bosch.io> * more refactoring after review Signed-off-by: Stanislav Trailov <Stanislav.Trailov@bosch.io> * skip compatibility check of dstype for retried rollout Signed-off-by: Stanislav Trailov <Stanislav.Trailov@bosch.io> * remove dsType from javadoc Signed-off-by: Stanislav Trailov <Stanislav.Trailov@bosch.io> --------- Signed-off-by: Stanislav Trailov <Stanislav.Trailov@bosch.io>
This commit is contained in:
committed by
GitHub
parent
22ce1c27a4
commit
44e7a72be3
@@ -139,6 +139,21 @@ public interface TargetManagement {
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
long countByRsqlAndCompatible(@NotEmpty String rsqlParam, @NotNull Long dsTypeId);
|
||||
|
||||
/**
|
||||
* Count all targets with failed actions for specific Rollout
|
||||
* and that are compatible with the passed {@link DistributionSetType}
|
||||
* and created after given timestamp
|
||||
*
|
||||
* @param rolloutId
|
||||
* rolloutId of the rollout to be retried.
|
||||
* @param dsTypeId
|
||||
* ID of the {@link DistributionSetType} the targets need to be
|
||||
* compatible with
|
||||
* @return the found number of{@link Target}s
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
long countByFailedInRollout(@NotEmpty String rolloutId, @NotNull Long dsTypeId);
|
||||
|
||||
/**
|
||||
* Count {@link TargetFilterQuery}s for given target filter query.
|
||||
*
|
||||
@@ -278,6 +293,23 @@ public interface TargetManagement {
|
||||
@NotEmpty Collection<Long> groups, @NotNull String rsqlParam,
|
||||
@NotNull DistributionSetType distributionSetType);
|
||||
|
||||
/**
|
||||
* Finds all targets with failed actions for specific Rollout
|
||||
* and that are not assigned to one of the retried {@link RolloutGroup}s and are
|
||||
* compatible with the passed {@link DistributionSetType}.
|
||||
*
|
||||
* @param pageRequest
|
||||
* the pageRequest to enhance the query for paging and sorting
|
||||
* @param groups
|
||||
* the list of {@link RolloutGroup}s
|
||||
* @param rolloutId
|
||||
* rolloutId of the rollout to be retried.
|
||||
* @return a page of the found {@link Target}s
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
Slice<Target> findByFailedRolloutAndNotInRolloutGroups(@NotNull Pageable pageRequest,
|
||||
@NotEmpty Collection<Long> groups, @NotNull String rolloutId);
|
||||
|
||||
/**
|
||||
* Counts all targets for all the given parameter {@link TargetFilterQuery}
|
||||
* and that are not assigned to one of the {@link RolloutGroup}s and are
|
||||
@@ -296,6 +328,20 @@ public interface TargetManagement {
|
||||
long countByRsqlAndNotInRolloutGroupsAndCompatible(@NotEmpty Collection<Long> groups, @NotNull String rsqlParam,
|
||||
@NotNull DistributionSetType distributionSetType);
|
||||
|
||||
/**
|
||||
* Counts all targets with failed actions for specific Rollout
|
||||
* and that are not assigned to one of the {@link RolloutGroup}s and are
|
||||
* compatible with the passed {@link DistributionSetType}.
|
||||
*
|
||||
* @param groups
|
||||
* the list of {@link RolloutGroup}s
|
||||
* @param rolloutId
|
||||
* rolloutId of the rollout to be retried.
|
||||
* @return count of the found {@link Target}s
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
long countByFailedRolloutAndNotInRolloutGroups(@NotEmpty Collection<Long> groups, @NotNull String rolloutId);
|
||||
|
||||
/**
|
||||
* Finds all targets of the provided {@link RolloutGroup} that have no
|
||||
* Action for the RolloutGroup.
|
||||
|
||||
@@ -229,6 +229,9 @@ public final class RolloutHelper {
|
||||
if (StringUtils.isEmpty(group.getTargetFilterQuery())) {
|
||||
return baseFilter;
|
||||
}
|
||||
if (isRolloutRetried(baseFilter)) {
|
||||
return baseFilter;
|
||||
}
|
||||
return concatAndTargetFilters(baseFilter, group.getTargetFilterQuery());
|
||||
}
|
||||
|
||||
@@ -253,4 +256,12 @@ public final class RolloutHelper {
|
||||
+ rollout.getStatus().name().toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isRolloutRetried(final String targetFilter) {
|
||||
return targetFilter.contains("failedrollout");
|
||||
}
|
||||
|
||||
public static String getIdFromRetriedTargetFilter(final String targetFilter) {
|
||||
return targetFilter.substring("failedrollout==".length());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,10 +525,18 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
final List<Long> readyGroups = RolloutHelper.getGroupsByStatusIncludingGroup(rollout.getRolloutGroups(),
|
||||
RolloutGroupStatus.READY, group);
|
||||
|
||||
final long targetsInGroupFilter = DeploymentHelper.runInNewTransaction(txManager,
|
||||
long targetsInGroupFilter;
|
||||
if (!RolloutHelper.isRolloutRetried(rollout.getTargetFilterQuery())) {
|
||||
targetsInGroupFilter = DeploymentHelper.runInNewTransaction(txManager,
|
||||
"countAllTargetsByTargetFilterQueryAndNotInRolloutGroups",
|
||||
count -> targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatible(readyGroups, groupTargetFilter,
|
||||
rollout.getDistributionSet().getType()));
|
||||
rollout.getDistributionSet().getType()));
|
||||
} else {
|
||||
targetsInGroupFilter = DeploymentHelper.runInNewTransaction(txManager,
|
||||
"countByFailedRolloutAndNotInRolloutGroupsAndCompatible",
|
||||
count -> targetManagement.countByFailedRolloutAndNotInRolloutGroups(readyGroups,
|
||||
RolloutHelper.getIdFromRetriedTargetFilter(rollout.getTargetFilterQuery())));
|
||||
}
|
||||
final long expectedInGroup = Math
|
||||
.round((double) (group.getTargetPercentage() / 100) * (double) targetsInGroupFilter);
|
||||
final long currentlyInGroup = DeploymentHelper.runInNewTransaction(txManager,
|
||||
@@ -572,8 +580,14 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
final PageRequest pageRequest = PageRequest.of(0, Math.toIntExact(limit));
|
||||
final List<Long> readyGroups = RolloutHelper.getGroupsByStatusIncludingGroup(rollout.getRolloutGroups(),
|
||||
RolloutGroupStatus.READY, group);
|
||||
final Slice<Target> targets = targetManagement.findByTargetFilterQueryAndNotInRolloutGroupsAndCompatible(
|
||||
Slice<Target> targets;
|
||||
if (!RolloutHelper.isRolloutRetried(rollout.getTargetFilterQuery())) {
|
||||
targets = targetManagement.findByTargetFilterQueryAndNotInRolloutGroupsAndCompatible(
|
||||
pageRequest, readyGroups, targetFilter, rollout.getDistributionSet().getType());
|
||||
} else {
|
||||
targets = targetManagement.findByFailedRolloutAndNotInRolloutGroups(
|
||||
pageRequest, readyGroups, RolloutHelper.getIdFromRetriedTargetFilter(rollout.getTargetFilterQuery()));
|
||||
}
|
||||
|
||||
createAssignmentOfTargetsToGroup(targets, group);
|
||||
|
||||
|
||||
@@ -196,10 +196,20 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
|
||||
private JpaRollout createRollout(final JpaRollout rollout) {
|
||||
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(rollout);
|
||||
final Long totalTargets = targetManagement.countByRsqlAndCompatible(rollout.getTargetFilterQuery(),
|
||||
long totalTargets;
|
||||
String errMsg;
|
||||
if (RolloutHelper.isRolloutRetried(rollout.getTargetFilterQuery())) {
|
||||
totalTargets = targetManagement.countByFailedInRollout(
|
||||
RolloutHelper.getIdFromRetriedTargetFilter(rollout.getTargetFilterQuery()),
|
||||
rollout.getDistributionSet().getType().getId());
|
||||
errMsg = "No failed targets in Rollout";
|
||||
} else {
|
||||
totalTargets = targetManagement.countByRsqlAndCompatible(rollout.getTargetFilterQuery(),
|
||||
rollout.getDistributionSet().getType().getId());
|
||||
errMsg = "Rollout does not match any existing targets";
|
||||
}
|
||||
if (totalTargets == 0) {
|
||||
throw new ValidationException("Rollout does not match any existing targets");
|
||||
throw new ValidationException(errMsg);
|
||||
}
|
||||
rollout.setTotalTargets(totalTargets);
|
||||
return rolloutRepository.save(rollout);
|
||||
@@ -618,10 +628,19 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
private RolloutGroupsValidation validateTargetsInGroups(final List<RolloutGroup> groups, final String baseFilter,
|
||||
final long totalTargets, final Long dsTypeId) {
|
||||
final List<Long> groupTargetCounts = new ArrayList<>(groups.size());
|
||||
final Map<String, Long> targetFilterCounts = groups.stream()
|
||||
Map<String, Long> targetFilterCounts;
|
||||
if (!RolloutHelper.isRolloutRetried(baseFilter)) {
|
||||
targetFilterCounts = groups.stream()
|
||||
.map(group -> RolloutHelper.getGroupTargetFilter(baseFilter, group)).distinct()
|
||||
.collect(Collectors.toMap(Function.identity(),
|
||||
groupTargetFilter -> targetManagement.countByRsqlAndCompatible(groupTargetFilter, dsTypeId)));
|
||||
groupTargetFilter -> targetManagement.countByRsqlAndCompatible(groupTargetFilter, dsTypeId)));
|
||||
} else {
|
||||
targetFilterCounts = groups.stream()
|
||||
.map(group -> RolloutHelper.getGroupTargetFilter(baseFilter, group)).distinct()
|
||||
.collect(Collectors.toMap(Function.identity(),
|
||||
groupTargetFilter -> targetManagement.countByFailedInRollout(
|
||||
RolloutHelper.getIdFromRetriedTargetFilter(baseFilter), dsTypeId)));
|
||||
}
|
||||
|
||||
long unusedTargetsCount = 0;
|
||||
|
||||
@@ -675,8 +694,11 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
|
||||
private long calculateRemainingTargets(final List<RolloutGroup> groups, final String targetFilter,
|
||||
final Long createdAt, final Long dsTypeId) {
|
||||
final String baseFilter = RolloutHelper.getTargetFilterQuery(targetFilter, createdAt);
|
||||
final long totalTargets = targetManagement.countByRsqlAndCompatible(baseFilter, dsTypeId);
|
||||
|
||||
final TargetCount targets = calculateTargets(targetFilter, createdAt, dsTypeId);
|
||||
long totalTargets = targets.total();
|
||||
final String baseFilter = targets.filter();
|
||||
|
||||
if (totalTargets == 0) {
|
||||
throw new ConstraintDeclarationException("Rollout target filter does not match any targets");
|
||||
}
|
||||
@@ -691,9 +713,9 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
public ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups(final List<RolloutGroupCreate> groups,
|
||||
final String targetFilter, final Long createdAt, final Long dsTypeId) {
|
||||
|
||||
final String baseFilter = RolloutHelper.getTargetFilterQuery(targetFilter, createdAt);
|
||||
|
||||
final long totalTargets = targetManagement.countByRsqlAndCompatible(baseFilter, dsTypeId);
|
||||
final TargetCount targets = calculateTargets(targetFilter, createdAt, dsTypeId);
|
||||
long totalTargets = targets.total();
|
||||
final String baseFilter = targets.filter();
|
||||
|
||||
if (totalTargets == 0) {
|
||||
throw new ConstraintDeclarationException("Rollout target filter does not match any targets");
|
||||
@@ -730,4 +752,21 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
startNextRolloutGroupAction.exec(rollout, latestRunning);
|
||||
}
|
||||
|
||||
private TargetCount calculateTargets(final String targetFilter, final Long createdAt, final Long dsTypeId) {
|
||||
String baseFilter;
|
||||
long totalTargets;
|
||||
if (!RolloutHelper.isRolloutRetried(targetFilter)) {
|
||||
baseFilter = RolloutHelper.getTargetFilterQuery(targetFilter, createdAt);
|
||||
totalTargets = targetManagement.countByRsqlAndCompatible(baseFilter, dsTypeId);
|
||||
} else {
|
||||
totalTargets = targetManagement.countByFailedInRollout(
|
||||
RolloutHelper.getIdFromRetriedTargetFilter(targetFilter), dsTypeId);
|
||||
baseFilter = targetFilter;
|
||||
}
|
||||
|
||||
return new TargetCount(totalTargets, baseFilter);
|
||||
}
|
||||
|
||||
private record TargetCount(long total, String filter) {}
|
||||
|
||||
}
|
||||
|
||||
@@ -693,6 +693,17 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageRequest, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Target> findByFailedRolloutAndNotInRolloutGroups(Pageable pageRequest, Collection<Long> groups,
|
||||
String rolloutId) {
|
||||
final List<Specification<JpaTarget>> specList = Arrays.asList(
|
||||
TargetSpecifications.failedActionsForRollout(rolloutId),
|
||||
TargetSpecifications.isNotInRolloutGroups(groups)
|
||||
);
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageRequest, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Target> findByInRolloutGroupWithoutAction(final Pageable pageRequest, final long group) {
|
||||
if (!rolloutGroupRepository.existsById(group)) {
|
||||
@@ -715,6 +726,15 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
return JpaManagementHelper.countBySpec(targetRepository, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByFailedRolloutAndNotInRolloutGroups(Collection<Long> groups, String rolloutId) {
|
||||
final List<Specification<JpaTarget>> specList = Arrays.asList(
|
||||
TargetSpecifications.failedActionsForRollout(rolloutId),
|
||||
TargetSpecifications.isNotInRolloutGroups(groups));
|
||||
|
||||
return JpaManagementHelper.countBySpec(targetRepository, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByRsqlAndNonDSAndCompatible(final long distributionSetId, final String targetFilterQuery) {
|
||||
final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
|
||||
@@ -796,6 +816,14 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
return JpaManagementHelper.countBySpec(targetRepository, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByFailedInRollout(final String rolloutId, final Long dsTypeId) {
|
||||
final List<Specification<JpaTarget>> specList = List.of(
|
||||
TargetSpecifications.failedActionsForRollout(rolloutId));
|
||||
|
||||
return JpaManagementHelper.countBySpec(targetRepository, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Target> get(final long id) {
|
||||
return targetRepository.findById(id).map(t -> t);
|
||||
|
||||
@@ -42,8 +42,10 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup_;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
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.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
@@ -611,4 +613,15 @@ public final class TargetSpecifications {
|
||||
};
|
||||
}
|
||||
|
||||
public static Specification<JpaTarget> failedActionsForRollout(final String rolloutId) {
|
||||
return (targetRoot, query, cb) -> {
|
||||
Join<JpaTarget, Action> targetActions =
|
||||
targetRoot.join("actions");
|
||||
|
||||
return cb.and(
|
||||
cb.equal(targetActions.get("rollout").get("id"), rolloutId),
|
||||
cb.equal(targetActions.get("status"), Action.Status.ERROR));
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user