Fix dynamic rollouts when there are finished actions from previous rollouts (#2434)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-06-06 16:13:21 +03:00
committed by GitHub
parent d166dd6224
commit 0e0b5ed6ff
6 changed files with 151 additions and 209 deletions

View File

@@ -253,12 +253,12 @@ public interface TargetManagement {
* @return a page of the found {@link Target}s * @return a page of the found {@link Target}s
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ)
Slice<Target> findByTargetFilterQueryAndNotInRolloutGroupsAndCompatibleAndUpdatable(@NotNull Pageable pageRequest, Slice<Target> findByTargetFilterQueryAndNotInRolloutAndCompatibleAndUpdatable(@NotNull Pageable pageRequest,
@NotEmpty Collection<Long> groups, @NotNull String targetFilterQuery, @NotEmpty Collection<Long> groups, @NotNull String targetFilterQuery,
@NotNull DistributionSetType distributionSetType); @NotNull DistributionSetType distributionSetType);
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ)
Slice<Target> findByNotInGEGroupAndNotInActiveActionGEWeightOrInRolloutAndTargetFilterQueryAndCompatibleAndUpdatable( Slice<Target> findByTargetFilterQueryAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
@NotNull Pageable pageRequest, final long rolloutId, final int weight, final long firstGroupId, @NotNull String targetFilterQuery, @NotNull Pageable pageRequest, final long rolloutId, final int weight, final long firstGroupId, @NotNull String targetFilterQuery,
@NotNull DistributionSetType distributionSetType); @NotNull DistributionSetType distributionSetType);

View File

@@ -647,7 +647,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
rollout.getRolloutGroups(), RolloutGroupStatus.READY, group); rollout.getRolloutGroups(), RolloutGroupStatus.READY, group);
final Slice<Target> targets; final Slice<Target> targets;
if (!RolloutHelper.isRolloutRetried(rollout.getTargetFilterQuery())) { if (!RolloutHelper.isRolloutRetried(rollout.getTargetFilterQuery())) {
targets = targetManagement.findByTargetFilterQueryAndNotInRolloutGroupsAndCompatibleAndUpdatable( targets = targetManagement.findByTargetFilterQueryAndNotInRolloutAndCompatibleAndUpdatable(
pageRequest, readyGroups, targetFilter, rollout.getDistributionSet().getType()); pageRequest, readyGroups, targetFilter, rollout.getDistributionSet().getType());
} else { } else {
targets = targetManagement.findByFailedRolloutAndNotInRolloutGroups( targets = targetManagement.findByFailedRolloutAndNotInRolloutGroups(
@@ -771,7 +771,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
final String targetFilter, final long limit) { final String targetFilter, final long limit) {
return DeploymentHelper.runInNewTransaction(txManager, "createActionsForRolloutDynamicGroup", status -> { return DeploymentHelper.runInNewTransaction(txManager, "createActionsForRolloutDynamicGroup", status -> {
final PageRequest pageRequest = PageRequest.of(0, Math.toIntExact(limit)); final PageRequest pageRequest = PageRequest.of(0, Math.toIntExact(limit));
final Slice<Target> targets = targetManagement.findByNotInGEGroupAndNotInActiveActionGEWeightOrInRolloutAndTargetFilterQueryAndCompatibleAndUpdatable( final Slice<Target> targets = targetManagement.findByTargetFilterQueryAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
pageRequest, pageRequest,
rollout.getId(), rollout.getWeight().orElse(1000), // Dynamic rollouts shall always have weight! rollout.getId(), rollout.getWeight().orElse(1000), // Dynamic rollouts shall always have weight!
rolloutGroupRepository.findByRolloutOrderByIdAsc(rollout).get(0).getId(), rolloutGroupRepository.findByRolloutOrderByIdAsc(rollout).get(0).getId(),

View File

@@ -12,7 +12,6 @@ package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.JpaManagementHelper.combineWithAnd; import static org.eclipse.hawkbit.repository.jpa.JpaManagementHelper.combineWithAnd;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@@ -135,7 +134,6 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public long countByAssignedDistributionSet(final long distributionSetId) { public long countByAssignedDistributionSet(final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distributionSetId)); final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distributionSetId));
return targetRepository.count(TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId())); return targetRepository.count(TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId()));
} }
@@ -148,29 +146,29 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public long countByInstalledDistributionSet(final long distributionSetId) { public long countByInstalledDistributionSet(final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distributionSetId)); final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distributionSetId));
return targetRepository.count(TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId())); return targetRepository.count(TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId()));
} }
@Override @Override
public boolean existsByInstalledOrAssignedDistributionSet(final long distributionSetId) { public boolean existsByInstalledOrAssignedDistributionSet(final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distributionSetId)); final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distributionSetId));
return targetRepository.exists(TargetSpecifications.hasInstalledOrAssignedDistributionSet(validDistSet.getId()));
return targetRepository
.exists(TargetSpecifications.hasInstalledOrAssignedDistributionSet(validDistSet.getId()));
} }
@Override @Override
public long countByRsql(final String targetFilterQuery) { public long countByRsql(final String targetFilterQuery) {
return JpaManagementHelper.countBySpec(targetRepository, List.of(RSQLUtility return JpaManagementHelper.countBySpec(
.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database))); targetRepository,
List.of(RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database)));
} }
@Override @Override
public long countByRsqlAndUpdatable(String targetFilterQuery) { public long countByRsqlAndUpdatable(String targetFilterQuery) {
final List<Specification<JpaTarget>> specList = List.of(RSQLUtility.buildRsqlSpecification(targetFilterQuery, final List<Specification<JpaTarget>> specList = List.of(
TargetFields.class, virtualPropertyReplacer, database)); RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database));
return targetRepository.count(AccessController.Operation.UPDATE, combineWithAnd(specList)); return targetRepository.count(
AccessController.Operation.UPDATE,
combineWithAnd(specList));
} }
@Override @Override
@@ -197,7 +195,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public long countByTargetFilterQuery(final long targetFilterQueryId) { public long countByTargetFilterQuery(final long targetFilterQueryId) {
final TargetFilterQuery targetFilterQuery = targetFilterQueryRepository.findById(targetFilterQueryId) final TargetFilterQuery targetFilterQuery = targetFilterQueryRepository
.findById(targetFilterQueryId)
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId)); .orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId));
return countByRsql(targetFilterQuery.getQuery()); return countByRsql(targetFilterQuery.getQuery());
} }
@@ -221,8 +220,7 @@ public class JpaTargetManagement implements TargetManagement {
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY)) backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<Target> create(final Collection<TargetCreate> targets) { public List<Target> create(final Collection<TargetCreate> targets) {
final List<JpaTarget> targetList = targets.stream().map(JpaTargetCreate.class::cast).map(JpaTargetCreate::build) final List<JpaTarget> targetList = targets.stream().map(JpaTargetCreate.class::cast).map(JpaTargetCreate::build).toList();
.toList();
return Collections.unmodifiableList(targetRepository.saveAll(AccessController.Operation.CREATE, targetList)); return Collections.unmodifiableList(targetRepository.saveAll(AccessController.Operation.CREATE, targetList));
} }
@@ -236,7 +234,6 @@ public class JpaTargetManagement implements TargetManagement {
throw new EntityNotFoundException(Target.class, ids, throw new EntityNotFoundException(Target.class, ids,
targets.stream().map(Target::getId).filter(id -> !ids.contains(id)).toList()); targets.stream().map(Target::getId).filter(id -> !ids.contains(id)).toList());
} }
targetRepository.deleteAll(targets); targetRepository.deleteAll(targets);
} }
@@ -255,10 +252,10 @@ public class JpaTargetManagement implements TargetManagement {
final Long distSetTypeId = jpaDistributionSet.getType().getId(); final Long distSetTypeId = jpaDistributionSet.getType().getId();
return targetRepository return targetRepository
.findAllWithoutCount(AccessController.Operation.UPDATE, .findAllWithoutCount(
AccessController.Operation.UPDATE,
combineWithAnd(List.of( combineWithAnd(List.of(
RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database),
virtualPropertyReplacer, database),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId), TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId))), TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId))),
pageRequest) pageRequest)
@@ -266,8 +263,7 @@ public class JpaTargetManagement implements TargetManagement {
} }
@Override @Override
public long countByRsqlAndNonDSAndCompatibleAndUpdatable(final long distributionSetId, public long countByRsqlAndNonDSAndCompatibleAndUpdatable(final long distributionSetId, final String targetFilterQuery) {
final String targetFilterQuery) {
final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId); final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final Long distSetTypeId = jpaDistributionSet.getType().getId(); final Long distSetTypeId = jpaDistributionSet.getType().getId();
@@ -281,14 +277,12 @@ public class JpaTargetManagement implements TargetManagement {
} }
@Override @Override
public Slice<Target> findByTargetFilterQueryAndNotInRolloutGroupsAndCompatibleAndUpdatable( public Slice<Target> findByTargetFilterQueryAndNotInRolloutAndCompatibleAndUpdatable(
final Pageable pageRequest, final Collection<Long> groups, final String targetFilterQuery, final Pageable pageRequest, final Collection<Long> groups, final String targetFilterQuery, final DistributionSetType dsType) {
final DistributionSetType dsType) {
return targetRepository return targetRepository
.findAllWithoutCount(AccessController.Operation.UPDATE, .findAllWithoutCount(AccessController.Operation.UPDATE,
combineWithAnd(List.of( combineWithAnd(List.of(
RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database),
virtualPropertyReplacer, database),
TargetSpecifications.isNotInRolloutGroups(groups), TargetSpecifications.isNotInRolloutGroups(groups),
TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))), TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))),
pageRequest) pageRequest)
@@ -296,16 +290,14 @@ public class JpaTargetManagement implements TargetManagement {
} }
@Override @Override
public Slice<Target> findByNotInGEGroupAndNotInActiveActionGEWeightOrInRolloutAndTargetFilterQueryAndCompatibleAndUpdatable( public Slice<Target> findByTargetFilterQueryAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
final Pageable pageRequest, final long rolloutId, final int weight, final long firstGroupId, final String targetFilterQuery, final Pageable pageRequest, final long rolloutId, final int weight, final long firstGroupId, final String targetFilterQuery,
final DistributionSetType distributionSetType) { final DistributionSetType distributionSetType) {
return targetRepository return targetRepository
.findAllWithoutCount(AccessController.Operation.UPDATE, .findAllWithoutCount(AccessController.Operation.UPDATE,
combineWithAnd(List.of( combineWithAnd(List.of(
RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database),
virtualPropertyReplacer, database), TargetSpecifications.hasNoOverridingActionsAndNotInRollout(weight, rolloutId),
TargetSpecifications.isNotInGERolloutGroup(firstGroupId),
TargetSpecifications.hasNoActiveActionWithGEWeightOrInRollout(weight, rolloutId),
TargetSpecifications.isCompatibleWithDistributionSetType(distributionSetType.getId()))), TargetSpecifications.isCompatibleWithDistributionSetType(distributionSetType.getId()))),
pageRequest) pageRequest)
.map(Target.class::cast); .map(Target.class::cast);
@@ -319,7 +311,7 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public Slice<Target> findByFailedRolloutAndNotInRolloutGroups(Pageable pageRequest, Collection<Long> groups, public Slice<Target> findByFailedRolloutAndNotInRolloutGroups(Pageable pageRequest, Collection<Long> groups,
String rolloutId) { String rolloutId) {
final List<Specification<JpaTarget>> specList = Arrays.asList( final List<Specification<JpaTarget>> specList = List.of(
TargetSpecifications.failedActionsForRollout(rolloutId), TargetSpecifications.failedActionsForRollout(rolloutId),
TargetSpecifications.isNotInRolloutGroups(groups)); TargetSpecifications.isNotInRolloutGroups(groups));
@@ -327,22 +319,20 @@ public class JpaTargetManagement implements TargetManagement {
} }
@Override @Override
public long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(final String targetFilterQuery, final Collection<Long> groups, public long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
final DistributionSetType dsType) { final String targetFilterQuery, final Collection<Long> groups, final DistributionSetType dsType) {
return targetRepository.count(AccessController.Operation.UPDATE, return targetRepository.count(AccessController.Operation.UPDATE,
combineWithAnd(List.of( combineWithAnd(List.of(
RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database),
virtualPropertyReplacer, database),
TargetSpecifications.isNotInRolloutGroups(groups), TargetSpecifications.isNotInRolloutGroups(groups),
TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId())))); TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))));
} }
@Override @Override
public long countByFailedRolloutAndNotInRolloutGroups(String rolloutId, Collection<Long> groups) { public long countByFailedRolloutAndNotInRolloutGroups(String rolloutId, Collection<Long> groups) {
final List<Specification<JpaTarget>> specList = Arrays.asList( final List<Specification<JpaTarget>> specList = List.of(
TargetSpecifications.failedActionsForRollout(rolloutId), TargetSpecifications.failedActionsForRollout(rolloutId),
TargetSpecifications.isNotInRolloutGroups(groups)); TargetSpecifications.isNotInRolloutGroups(groups));
return JpaManagementHelper.countBySpec(targetRepository, specList); return JpaManagementHelper.countBySpec(targetRepository, specList);
} }
@@ -352,22 +342,21 @@ public class JpaTargetManagement implements TargetManagement {
throw new EntityNotFoundException(RolloutGroup.class, group); throw new EntityNotFoundException(RolloutGroup.class, group);
} }
return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageRequest, return JpaManagementHelper.findAllWithoutCountBySpec(
List.of(TargetSpecifications.hasNoActionInRolloutGroup(group))); targetRepository, pageRequest, List.of(TargetSpecifications.hasNoActionInRolloutGroup(group)));
} }
@Override @Override
public Page<Target> findByAssignedDistributionSet(final Pageable pageReq, final long distributionSetId) { public Page<Target> findByAssignedDistributionSet(final Pageable pageReq, final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId); final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, return JpaManagementHelper.findAllWithCountBySpec(
List.of(TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId())), pageReq targetRepository,
); List.of(TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId())), pageReq);
} }
@Override @Override
public Page<Target> findByAssignedDistributionSetAndRsql(final Pageable pageReq, final long distributionSetId, public Page<Target> findByAssignedDistributionSetAndRsql(final Pageable pageReq, final long distributionSetId, final String rsqlParam) {
final String rsqlParam) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId); final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final List<Specification<JpaTarget>> specList = List.of( final List<Specification<JpaTarget>> specList = List.of(
@@ -379,8 +368,7 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public List<Target> getByControllerID(final Collection<String> controllerIDs) { public List<Target> getByControllerID(final Collection<String> controllerIDs) {
return Collections.unmodifiableList( return Collections.unmodifiableList(targetRepository.findAll(TargetSpecifications.byControllerIdWithAssignedDsInJoin(controllerIDs)));
targetRepository.findAll(TargetSpecifications.byControllerIdWithAssignedDsInJoin(controllerIDs)));
} }
@Override @Override
@@ -403,14 +391,12 @@ public class JpaTargetManagement implements TargetManagement {
public Page<Target> findByInstalledDistributionSet(final Pageable pageReq, final long distributionSetId) { public Page<Target> findByInstalledDistributionSet(final Pageable pageReq, final long distributionSetId) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId); final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, return JpaManagementHelper.findAllWithCountBySpec(
List.of(TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId())), pageReq targetRepository, List.of(TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId())), pageReq);
);
} }
@Override @Override
public Page<Target> findByInstalledDistributionSetAndRsql(final Pageable pageable, final long distributionSetId, public Page<Target> findByInstalledDistributionSetAndRsql(final Pageable pageable, final long distributionSetId, final String rsqlParam) {
final String rsqlParam) {
final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId); final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId);
final List<Specification<JpaTarget>> specList = List.of( final List<Specification<JpaTarget>> specList = List.of(
@@ -422,9 +408,8 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public Page<Target> findByUpdateStatus(final Pageable pageable, final TargetUpdateStatus status) { public Page<Target> findByUpdateStatus(final Pageable pageable, final TargetUpdateStatus status) {
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, List.of(TargetSpecifications.hasTargetUpdateStatus(status)), return JpaManagementHelper.findAllWithCountBySpec(
pageable targetRepository, List.of(TargetSpecifications.hasTargetUpdateStatus(status)), pageable);
);
} }
@Override @Override
@@ -434,8 +419,9 @@ public class JpaTargetManagement implements TargetManagement {
@Override @Override
public Slice<Target> findByRsql(final Pageable pageable, final String targetFilterQuery) { public Slice<Target> findByRsql(final Pageable pageable, final String targetFilterQuery) {
return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageable, List.of(RSQLUtility return JpaManagementHelper.findAllWithoutCountBySpec(
.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database))); targetRepository, pageable,
List.of(RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database)));
} }
@Override @Override
@@ -443,24 +429,23 @@ public class JpaTargetManagement implements TargetManagement {
final TargetFilterQuery targetFilterQuery = targetFilterQueryRepository.findById(targetFilterQueryId) final TargetFilterQuery targetFilterQuery = targetFilterQueryRepository.findById(targetFilterQueryId)
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId)); .orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId));
return JpaManagementHelper.findAllWithoutCountBySpec(targetRepository, pageable, return JpaManagementHelper.findAllWithoutCountBySpec(
List.of(RSQLUtility.buildRsqlSpecification(targetFilterQuery.getQuery(), TargetFields.class, targetRepository, pageable,
virtualPropertyReplacer, database))); List.of(RSQLUtility.buildRsqlSpecification(
targetFilterQuery.getQuery(), TargetFields.class, virtualPropertyReplacer, database)));
} }
@Override @Override
public Page<Target> findByTag(final Pageable pageable, final long tagId) { public Page<Target> findByTag(final Pageable pageable, final long tagId) {
throwEntityNotFoundExceptionIfTagDoesNotExist(tagId); throwEntityNotFoundExceptionIfTagDoesNotExist(tagId);
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, List.of(TargetSpecifications.hasTag(tagId)), pageable);
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, List.of(TargetSpecifications.hasTag(tagId)), pageable
);
} }
@Override @Override
public Page<Target> findByRsqlAndTag(final Pageable pageable, final String rsqlParam, final long tagId) { public Page<Target> findByRsqlAndTag(final Pageable pageable, final String rsqlParam, final long tagId) {
throwEntityNotFoundExceptionIfTagDoesNotExist(tagId); throwEntityNotFoundExceptionIfTagDoesNotExist(tagId);
final List<Specification<JpaTarget>> specList = Arrays.asList( final List<Specification<JpaTarget>> specList = List.of(
RSQLUtility.buildRsqlSpecification(rsqlParam, TargetFields.class, virtualPropertyReplacer, database), RSQLUtility.buildRsqlSpecification(rsqlParam, TargetFields.class, virtualPropertyReplacer, database),
TargetSpecifications.hasTag(tagId)); TargetSpecifications.hasTag(tagId));
@@ -472,20 +457,19 @@ public class JpaTargetManagement implements TargetManagement {
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY)) backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public TargetTypeAssignmentResult assignType(final Collection<String> controllerIds, final Long typeId) { public TargetTypeAssignmentResult assignType(final Collection<String> controllerIds, final Long typeId) {
final JpaTargetType type = targetTypeRepository.findById(typeId) final JpaTargetType type = targetTypeRepository
.findById(typeId)
.orElseThrow(() -> new EntityNotFoundException(TargetType.class, typeId)); .orElseThrow(() -> new EntityNotFoundException(TargetType.class, typeId));
final List<JpaTarget> targetsWithSameType = findTargetsByInSpecification(controllerIds, final List<JpaTarget> targetsWithSameType = findTargetsByInSpecification(controllerIds, TargetSpecifications.hasTargetType(typeId));
TargetSpecifications.hasTargetType(typeId)); final List<JpaTarget> targetsWithoutSameType =
findTargetsByInSpecification(controllerIds, TargetSpecifications.hasTargetTypeNot(typeId));
final List<JpaTarget> targetsWithoutSameType = findTargetsByInSpecification(controllerIds,
TargetSpecifications.hasTargetTypeNot(typeId));
// set new target type to all targets without that type // set new target type to all targets without that type
targetsWithoutSameType.forEach(target -> target.setTargetType(type)); targetsWithoutSameType.forEach(target -> target.setTargetType(type));
final TargetTypeAssignmentResult result = new TargetTypeAssignmentResult(targetsWithSameType.size(), final TargetTypeAssignmentResult result = new TargetTypeAssignmentResult(
targetRepository.saveAll(targetsWithoutSameType), Collections.emptyList(), type); targetsWithSameType.size(), targetRepository.saveAll(targetsWithoutSameType), Collections.emptyList(), type);
// no reason to persist the type // no reason to persist the type
entityManager.detach(type); entityManager.detach(type);
@@ -500,8 +484,7 @@ public class JpaTargetManagement implements TargetManagement {
final List<JpaTarget> allTargets = findTargetsByInSpecification(controllerIds, null); final List<JpaTarget> allTargets = findTargetsByInSpecification(controllerIds, null);
if (allTargets.size() < controllerIds.size()) { if (allTargets.size() < controllerIds.size()) {
throw new EntityNotFoundException(Target.class, controllerIds, throw new EntityNotFoundException(Target.class, controllerIds, allTargets.stream().map(Target::getControllerId).toList());
allTargets.stream().map(Target::getControllerId).toList());
} }
// set new target type to null for all targets // set new target type to null for all targets
@@ -514,8 +497,8 @@ public class JpaTargetManagement implements TargetManagement {
@Transactional @Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY)) backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<Target> assignTag(final Collection<String> controllerIds, final long targetTagId, public List<Target> assignTag(
final Consumer<Collection<String>> notFoundHandler) { final Collection<String> controllerIds, final long targetTagId, final Consumer<Collection<String>> notFoundHandler) {
return assignTag0(controllerIds, targetTagId, notFoundHandler); return assignTag0(controllerIds, targetTagId, notFoundHandler);
} }
@@ -527,8 +510,8 @@ public class JpaTargetManagement implements TargetManagement {
return assignTag0(controllerIds, targetTagId, null); return assignTag0(controllerIds, targetTagId, null);
} }
private List<Target> assignTag0(final Collection<String> controllerIds, final long targetTagId, private List<Target> assignTag0(
final Consumer<Collection<String>> notFoundHandler) { final Collection<String> controllerIds, final long targetTagId, final Consumer<Collection<String>> notFoundHandler) {
return updateTag(controllerIds, targetTagId, notFoundHandler, (tag, target) -> { return updateTag(controllerIds, targetTagId, notFoundHandler, (tag, target) -> {
if (target.getTags().contains(tag)) { if (target.getTags().contains(tag)) {
return target; return target;
@@ -543,8 +526,8 @@ public class JpaTargetManagement implements TargetManagement {
@Transactional @Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY)) backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public List<Target> unassignTag(final Collection<String> controllerIds, final long targetTagId, public List<Target> unassignTag(
final Consumer<Collection<String>> notFoundHandler) { final Collection<String> controllerIds, final long targetTagId, final Consumer<Collection<String>> notFoundHandler) {
return unassignTag0(controllerIds, targetTagId, notFoundHandler); return unassignTag0(controllerIds, targetTagId, notFoundHandler);
} }
@@ -556,8 +539,8 @@ public class JpaTargetManagement implements TargetManagement {
return unassignTag0(controllerIds, targetTagId, null); return unassignTag0(controllerIds, targetTagId, null);
} }
private List<Target> unassignTag0(final Collection<String> controllerIds, final long targetTagId, private List<Target> unassignTag0(
final Consumer<Collection<String>> notFoundHandler) { final Collection<String> controllerIds, final long targetTagId, final Consumer<Collection<String>> notFoundHandler) {
return updateTag(controllerIds, targetTagId, notFoundHandler, (tag, target) -> { return updateTag(controllerIds, targetTagId, notFoundHandler, (tag, target) -> {
if (target.getTags().contains(tag)) { if (target.getTags().contains(tag)) {
target.removeTag(tag); target.removeTag(tag);
@@ -630,13 +613,13 @@ public class JpaTargetManagement implements TargetManagement {
} }
@Override @Override
public boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(final String controllerId, public boolean isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
final long distributionSetId, final String targetFilterQuery) { final String controllerId, final long distributionSetId, final String targetFilterQuery) {
RSQLUtility.validateRsqlFor(targetFilterQuery, TargetFields.class, JpaTarget.class, virtualPropertyReplacer, entityManager); RSQLUtility.validateRsqlFor(targetFilterQuery, TargetFields.class, JpaTarget.class, virtualPropertyReplacer, entityManager);
final DistributionSet ds = distributionSetManagement.get(distributionSetId) final DistributionSet ds = distributionSetManagement.get(distributionSetId)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, distributionSetId)); .orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, distributionSetId));
final Long distSetTypeId = ds.getType().getId(); final Long distSetTypeId = ds.getType().getId();
final List<Specification<JpaTarget>> specList = Arrays.asList( final List<Specification<JpaTarget>> specList = List.of(
RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database), RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database),
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId), TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId), TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId),
@@ -830,8 +813,7 @@ public class JpaTargetManagement implements TargetManagement {
return specList; return specList;
} }
private List<JpaTarget> findTargetsByInSpecification(final Collection<String> controllerIds, private List<JpaTarget> findTargetsByInSpecification(final Collection<String> controllerIds, final Specification<JpaTarget> specification) {
final Specification<JpaTarget> specification) {
return ListUtils.partition(new ArrayList<>(controllerIds), Constants.MAX_ENTRIES_IN_STATEMENT).stream() return ListUtils.partition(new ArrayList<>(controllerIds), Constants.MAX_ENTRIES_IN_STATEMENT).stream()
.map(ids -> targetRepository.findAll(TargetSpecifications.hasControllerIdIn(ids).and(specification))) .map(ids -> targetRepository.findAll(TargetSpecifications.hasControllerIdIn(ids).and(specification)))
.flatMap(List::stream).toList(); .flatMap(List::stream).toList();

View File

@@ -119,15 +119,13 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link JpaTarget}s including * {@link Specification} for retrieving {@link JpaTarget}s including {@link JpaTarget#getAssignedDistributionSet()}.
* {@link JpaTarget#getAssignedDistributionSet()}.
* *
* @param controllerIDs to search for * @param controllerIDs to search for
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
*/ */
public static Specification<JpaTarget> byControllerIdWithAssignedDsInJoin(final Collection<String> controllerIDs) { public static Specification<JpaTarget> byControllerIdWithAssignedDsInJoin(final Collection<String> controllerIDs) {
return (targetRoot, query, cb) -> { return (targetRoot, query, cb) -> {
final Predicate predicate = targetRoot.get(JpaTarget_.controllerId).in(controllerIDs); final Predicate predicate = targetRoot.get(JpaTarget_.controllerId).in(controllerIDs);
targetRoot.fetch(JpaTarget_.assignedDistributionSet, JoinType.LEFT); targetRoot.fetch(JpaTarget_.assignedDistributionSet, JoinType.LEFT);
return predicate; return predicate;
@@ -135,8 +133,7 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s by "equal to any * {@link Specification} for retrieving {@link Target}s by "equal to any given {@link TargetUpdateStatus}".
* given {@link TargetUpdateStatus}".
* *
* @param updateStatus to be filtered on * @param updateStatus to be filtered on
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -146,8 +143,7 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s by "equal to given * {@link Specification} for retrieving {@link Target}s by "equal to given {@link TargetUpdateStatus}".
* {@link TargetUpdateStatus}".
* *
* @param updateStatus to be filtered on * @param updateStatus to be filtered on
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -157,8 +153,7 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s by "not equal to * {@link Specification} for retrieving {@link Target}s by "not equal to given {@link TargetUpdateStatus}".
* given {@link TargetUpdateStatus}".
* *
* @param updateStatus to be filtered on * @param updateStatus to be filtered on
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -168,17 +163,13 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s that are overdue. A * {@link Specification} for retrieving {@link Target}s that are overdue. A target is overdue if it did not respond during the configured
* target is overdue if it did not respond during the configured
* intervals:<br> * intervals:<br>
* <em>poll_itvl + overdue_itvl</em> * <em>poll_itvl + overdue_itvl</em>
* *
* @param overdueTimestamp the calculated timestamp to compare with the last respond of a * @param overdueTimestamp the calculated timestamp to compare with the last response of a target (lastTargetQuery).<br>
* target (lastTargetQuery).<br> * The <code>overdueTimestamp</code> has to be calculated with the following expression:<br>
* The <code>overdueTimestamp</code> has to be calculated with * <em>overdueTimestamp = nowTimestamp - poll_itvl - overdue_itvl</em>
* the following expression:<br>
* <em>overdueTimestamp = nowTimestamp - poll_itvl -
* overdue_itvl</em>
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
*/ */
public static Specification<JpaTarget> isOverdue(final long overdueTimestamp) { public static Specification<JpaTarget> isOverdue(final long overdueTimestamp) {
@@ -187,8 +178,7 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s by "like * {@link Specification} for retrieving {@link Target}s by "like controllerId or like name".
* controllerId or like name".
* *
* @param searchText to be filtered on * @param searchText to be filtered on
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -202,8 +192,7 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s by "like * {@link Specification} for retrieving {@link Target}s by "like controllerId".
* controllerId".
* *
* @param distributionId to be filtered on * @param distributionId to be filtered on
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -228,8 +217,7 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s based on a * {@link Specification} for retrieving {@link Target}s based on a {@link TargetTag} name.
* {@link TargetTag} name.
* *
* @param tagName to search for * @param tagName to search for
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -242,8 +230,7 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s by "has no tag * {@link Specification} for retrieving {@link Target}s by "has no tag names"or "has at least on of the given tag names".
* names"or "has at least on of the given tag names".
* *
* @param tagNames to be filtered on * @param tagNames to be filtered on
* @param selectTargetWithNoTag flag to get targets with no tag assigned * @param selectTargetWithNoTag flag to get targets with no tag assigned
@@ -258,8 +245,7 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s by assigned * {@link Specification} for retrieving {@link Target}s by assigned distribution set.
* distribution set.
* *
* @param distributionSetId the ID of the distribution set which must be assigned * @param distributionSetId the ID of the distribution set which must be assigned
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -270,8 +256,7 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s that don't have the * {@link Specification} for retrieving {@link Target}s that don't have the given distribution set in their action history
* given distribution set in their action history
* *
* @param distributionSetId the ID of the distribution set which must not be assigned * @param distributionSetId the ID of the distribution set which must not be assigned
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -285,10 +270,9 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s that are compatible * {@link Specification} for retrieving {@link Target}s that are compatible with given {@link DistributionSetType}. Compatibility is
* with given {@link DistributionSetType}. Compatibility is evaluated by * evaluated by checking the {@link TargetType} of a target. Targets that don't have a {@link TargetType} are compatible with all
* checking the {@link TargetType} of a target. Targets that don't have a * {@link DistributionSetType}
* {@link TargetType} are compatible with all {@link DistributionSetType}
* *
* @param distributionSetTypeId the ID of the distribution set type which must be compatible * @param distributionSetTypeId the ID of the distribution set type which must be compatible
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -302,10 +286,8 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s that are NOT * {@link Specification} for retrieving {@link Target}s that are NOT compatible with given {@link DistributionSetType}. Compatibility is
* compatible with given {@link DistributionSetType}. Compatibility is * evaluated by checking the {@link TargetType} of a target. Targets that don't have a {@link TargetType} are compatible with all
* evaluated by checking the {@link TargetType} of a target. Targets that
* don't have a {@link TargetType} are compatible with all
* {@link DistributionSetType} * {@link DistributionSetType}
* *
* @param distributionSetTypeId the ID of the distribution set type which must be incompatible * @param distributionSetTypeId the ID of the distribution set type which must be incompatible
@@ -329,23 +311,20 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s that are in a given * {@link Specification} for retrieving {@link Target}s that are in a given {@link RolloutGroup}
* {@link RolloutGroup}
* *
* @param group the {@link RolloutGroup} * @param group the {@link RolloutGroup}
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
*/ */
public static Specification<JpaTarget> isInRolloutGroup(final Long group) { public static Specification<JpaTarget> isInRolloutGroup(final Long group) {
return (targetRoot, query, cb) -> { return (targetRoot, query, cb) -> {
final ListJoin<JpaTarget, RolloutTargetGroup> targetGroupJoin = targetRoot final ListJoin<JpaTarget, RolloutTargetGroup> targetGroupJoin = targetRoot.join(JpaTarget_.rolloutTargetGroup);
.join(JpaTarget_.rolloutTargetGroup);
return cb.equal(targetGroupJoin.get(RolloutTargetGroup_.rolloutGroup).get(AbstractJpaBaseEntity_.id), group); return cb.equal(targetGroupJoin.get(RolloutTargetGroup_.rolloutGroup).get(AbstractJpaBaseEntity_.id), group);
}; };
} }
/** /**
* {@link Specification} for retrieving {@link Target}s that are in an * {@link Specification} for retrieving {@link Target}s that are in an action for a given {@link RolloutGroup}
* action for a given {@link RolloutGroup}
* *
* @param group the {@link RolloutGroup} * @param group the {@link RolloutGroup}
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -358,41 +337,21 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s that are not in the * {@link Specification} for retrieving {@link Target}s that are not in the given {@link RolloutGroup}s
* given {@link RolloutGroup}s
* *
* @param groups the {@link RolloutGroup}s * @param groups the {@link RolloutGroup}s
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
*/ */
public static Specification<JpaTarget> isNotInRolloutGroups(final Collection<Long> groups) { public static Specification<JpaTarget> isNotInRolloutGroups(final Collection<Long> groups) {
return (targetRoot, query, cb) -> { return (targetRoot, query, cb) -> {
final ListJoin<JpaTarget, RolloutTargetGroup> rolloutTargetJoin = targetRoot final ListJoin<JpaTarget, RolloutTargetGroup> rolloutTargetJoin = targetRoot.join(JpaTarget_.rolloutTargetGroup, JoinType.LEFT);
.join(JpaTarget_.rolloutTargetGroup, JoinType.LEFT); rolloutTargetJoin.on(rolloutTargetJoin.get(RolloutTargetGroup_.rolloutGroup).get(AbstractJpaBaseEntity_.id).in(groups));
rolloutTargetJoin.on(rolloutTargetJoin.get(RolloutTargetGroup_.rolloutGroup)
.get(AbstractJpaBaseEntity_.id).in(groups));
return cb.isNull(rolloutTargetJoin.get(RolloutTargetGroup_.target)); return cb.isNull(rolloutTargetJoin.get(RolloutTargetGroup_.target));
}; };
} }
/** /**
* {@link Specification} for retrieving {@link Target}s that are not in * {@link Specification} for retrieving {@link Target}s that have no Action of the {@link RolloutGroup}.
* any {@link RolloutGroup}s
*
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> isNotInGERolloutGroup(final long groupId) {
return (targetRoot, query, cb) -> {
final ListJoin<JpaTarget, RolloutTargetGroup> rolloutTargetJoin = targetRoot
.join(JpaTarget_.rolloutTargetGroup, JoinType.LEFT);
rolloutTargetJoin.on(cb.ge(rolloutTargetJoin.get(RolloutTargetGroup_.rolloutGroup)
.get(AbstractJpaBaseEntity_.id), groupId));
return cb.isNull(rolloutTargetJoin.get(RolloutTargetGroup_.target));
};
}
/**
* {@link Specification} for retrieving {@link Target}s that have no Action
* of the {@link RolloutGroup}.
* *
* @param group the {@link RolloutGroup} * @param group the {@link RolloutGroup}
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -412,8 +371,7 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s by assigned * {@link Specification} for retrieving {@link Target}s by assigned distribution set.
* distribution set.
* *
* @param distributionSetId the ID of the distribution set which must be assigned * @param distributionSetId the ID of the distribution set which must be assigned
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -448,8 +406,7 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s by target type id is * {@link Specification} for retrieving {@link Target}s by target type id is equal to null
* equal to null
* *
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
*/ */
@@ -458,8 +415,7 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s that don't have * {@link Specification} for retrieving {@link Target}s that don't have target type assigned
* target type assigned
* *
* @param typeId the id of the target type * @param typeId the id of the target type
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
@@ -471,9 +427,7 @@ public final class TargetSpecifications {
public static Specification<JpaTarget> failedActionsForRollout(final String rolloutId) { public static Specification<JpaTarget> failedActionsForRollout(final String rolloutId) {
return (targetRoot, query, cb) -> { return (targetRoot, query, cb) -> {
Join<JpaTarget, Action> targetActions = final Join<JpaTarget, Action> targetActions = targetRoot.join("actions");
targetRoot.join("actions");
return cb.and( return cb.and(
cb.equal(targetActions.get("rollout").get("id"), rolloutId), cb.equal(targetActions.get("rollout").get("id"), rolloutId),
cb.equal(targetActions.get("status"), Action.Status.ERROR)); cb.equal(targetActions.get("status"), Action.Status.ERROR));
@@ -481,34 +435,31 @@ public final class TargetSpecifications {
} }
/** /**
* {@link Specification} for retrieving {@link Target}s that have no active (non-finished) action * {@link Specification} for retrieving {@link Target}s that have:
* with great or equal weight (GEWeight. * <ul>
* <li>no active (non-finished) actions with greater weight in the older rollouts</li>
* <li>or have actions in the current rollout</li>
* <li>or have actions with great or equal weight in the newer rollouts</li>
* </ul>
* *
* @param weight the referent weight * @param weight the referent weight
* @return the {@link Target} {@link Specification} * @return the {@link Target} {@link Specification}
*/ */
public static Specification<JpaTarget> hasNoActiveActionWithGEWeightOrInRollout(final int weight, final long rolloutId) { public static Specification<JpaTarget> hasNoOverridingActionsAndNotInRollout(final int weight, final long rolloutId) {
return (targetRoot, query, cb) -> { return (targetRoot, query, cb) -> {
final ListJoin<JpaTarget, JpaAction> actionsJoin = targetRoot.join(JpaTarget_.actions, JoinType.LEFT); final ListJoin<JpaTarget, JpaAction> actionsJoin = targetRoot.join(JpaTarget_.actions, JoinType.LEFT);
actionsJoin.on( actionsJoin.on(
cb.or( cb.or(
cb.gt(actionsJoin.get(JpaAction_.weight), weight),
cb.and( cb.and(
cb.equal(actionsJoin.get(JpaAction_.weight), weight), cb.and(
cb.ge(actionsJoin.get(JpaAction_.ROLLOUT).get(AbstractJpaBaseEntity_.ID), rolloutId)))); cb.lt(actionsJoin.get(JpaAction_.rollout).get(AbstractJpaBaseEntity_.id), rolloutId),
// another, but probably heavier variant cb.gt(actionsJoin.get(JpaAction_.weight), weight)),
// actionsJoin.on( cb.or(
// cb.or( cb.equal(actionsJoin.get(JpaAction_.active), true),
// // in rollout cb.equal(actionsJoin.get(JpaAction_.status), Action.Status.SCHEDULED))),
// cb.equal(actionsJoin.get(JpaAction_.ROLLOUT).get(AbstractJpaBaseEntity_.ID), rolloutId), cb.and(
// // or, in newer rollout with greater or equal weight cb.ge(actionsJoin.get(JpaAction_.rollout).get(AbstractJpaBaseEntity_.id), rolloutId),
// cb.and( cb.ge(actionsJoin.get(JpaAction_.weight), weight))));
// cb.gt(actionsJoin.get(JpaAction_.ROLLOUT).get(AbstractJpaBaseEntity_.ID), rolloutId),
// cb.ge(actionsJoin.get(JpaAction_.weight), weight)),
// // or, in older with greater status
// cb.and(
// cb.lt(actionsJoin.get(JpaAction_.ROLLOUT).get(AbstractJpaBaseEntity_.ID), rolloutId),
// cb.gt(actionsJoin.get(JpaAction_.weight), weight))));
return cb.isNull(actionsJoin.get(AbstractJpaBaseEntity_.id)); return cb.isNull(actionsJoin.get(AbstractJpaBaseEntity_.id));
}; };
} }

View File

@@ -147,9 +147,9 @@ class TargetManagementSecurityTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.") @Description("Tests ManagementAPI PreAuthorized method with correct and insufficient permissions.")
void findByTargetFilterQueryAndNotInRolloutGroupsAndCompatibleAndUpdatablePermissionsCheck() { void findByTargetFilterQueryAndNotInRolloutAndCompatibleAndUpdatablePermissionsCheck() {
assertPermissions( assertPermissions(
() -> targetManagement.findByTargetFilterQueryAndNotInRolloutGroupsAndCompatibleAndUpdatable(PAGE, List.of(1L), () -> targetManagement.findByTargetFilterQueryAndNotInRolloutAndCompatibleAndUpdatable(PAGE, List.of(1L),
"controllerId==id", "controllerId==id",
entityFactory.distributionSetType().create().build()), List.of(SpPermission.READ_TARGET, SpPermission.READ_ROLLOUT)); entityFactory.distributionSetType().create().build()), List.of(SpPermission.READ_TARGET, SpPermission.READ_ROLLOUT));
} }

View File

@@ -789,8 +789,6 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
.isThrownBy(() -> targetManagement.createMetadata(target3ControllerId, metaData3)); .isThrownBy(() -> targetManagement.createMetadata(target3ControllerId, metaData3));
} }
@Test @Test
@Description("Queries and loads the metadata related to a given target.") @Description("Queries and loads the metadata related to a given target.")
void getMetadata() { void getMetadata() {
@@ -1114,8 +1112,8 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
void matchesFilterTargetNotExists() { void matchesFilterTargetNotExists() {
final DistributionSet ds = testdataFactory.createDistributionSet(); final DistributionSet ds = testdataFactory.createDistributionSet();
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable("notExisting", ds.getId(), assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatibleAndUpdatable(
"name==*")).isFalse(); "notExisting", ds.getId(),"name==*")).isFalse();
} }
/** /**
@@ -1123,43 +1121,53 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
*/ */
@Test @Test
@Description("Target matches filter no active action with ge weight.") @Description("Target matches filter no active action with ge weight.")
void findByNotInGEGroupAndNotInActiveActionGEWeightOrInRolloutAndTargetFilterQueryAndCompatibleAndUpdatable() { void findByTargetFilterQueryAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable() {
final String targetPrefix = "dyn_action_filter_"; final String targetPrefix = "dyn_action_filter_";
final DistributionSet distributionSet = testdataFactory.createDistributionSet(); final DistributionSet distributionSet = testdataFactory.createDistributionSet();
final List<Target> targets = testdataFactory.createTargets(targetPrefix, 9); final List<Target> targets = testdataFactory.createTargets(targetPrefix, 10);
final Rollout rolloutOlder = testdataFactory.createRollout(); final Rollout rolloutOlder = testdataFactory.createRollout();
final Rollout rollout = testdataFactory.createRollout(); final Rollout rollout = testdataFactory.createRollout();
final Rollout rolloutNewer = testdataFactory.createRollout(); final Rollout rolloutNewer = testdataFactory.createRollout();
int target = 0;
final List<Integer> expected = new ArrayList<>();
// old ro with less weight - match // old ro with less weight - match
createAction(targets.get(0), rolloutOlder, 0, Status.RUNNING, distributionSet); expected.add(target);
// old ro with less weight - match createAction(targets.get(target++), rolloutOlder, 0, Status.RUNNING, distributionSet);
createAction(targets.get(1), rolloutOlder, 5, Status.SCHEDULED, distributionSet);
// old ro with equal weight - match // old ro with equal weight - match
createAction(targets.get(2), rolloutOlder, 10, Status.RUNNING, distributionSet); expected.add(target);
// old ro with BIGGER weight - doesn't match createAction(targets.get(target++), rolloutOlder, 10, Status.RUNNING, distributionSet);
createAction(targets.get(3), rolloutOlder, 20, Status.WAIT_FOR_CONFIRMATION, distributionSet); // old ro with bigger weight, scheduled - doesn't match
createAction(targets.get(target++), rolloutOlder, 11, Status.SCHEDULED, distributionSet);
// old ro with bigger weight, running - doesn't match
createAction(targets.get(target++), rolloutOlder, 11, Status.RUNNING, distributionSet);
// old ro with bigger weight, running match
expected.add(target);
createAction(targets.get(target++), rolloutOlder, 11, Status.FINISHED, distributionSet);
// same ro - doesn't match // same ro - doesn't match
createAction(targets.get(4), rollout, 10, Status.RUNNING, distributionSet); createAction(targets.get(target++), rollout, 10, Status.RUNNING, distributionSet);
// new ro with less weight - match // new ro with less weight - match
createAction(targets.get(5), rolloutNewer, 0, Status.RUNNING, distributionSet); expected.add(target);
createAction(targets.get(target++), rolloutNewer, 0, Status.RUNNING, distributionSet);
// new ro with less weight - match // new ro with less weight - match
createAction(targets.get(6), rolloutNewer, 5, Status.WARNING, distributionSet); expected.add(target);
createAction(targets.get(target++), rolloutNewer, 5, Status.WARNING, distributionSet);
// NEW ro with EQUAL weight - doesn't match // NEW ro with EQUAL weight - doesn't match
createAction(targets.get(7), rolloutNewer, 10, Status.RUNNING, distributionSet); createAction(targets.get(target++), rolloutNewer, 10, Status.RUNNING, distributionSet);
// new ro with BIGGER weight - doesn't match // new ro with BIGGER weight - doesn't match
createAction(targets.get(8), rolloutNewer, 20, Status.DOWNLOADED, distributionSet); createAction(targets.get(target), rolloutNewer, 20, Status.DOWNLOADED, distributionSet);
final Slice<Target> matching = targetManagement.findByNotInGEGroupAndNotInActiveActionGEWeightOrInRolloutAndTargetFilterQueryAndCompatibleAndUpdatable( final Slice<Target> matching =
targetManagement.findByTargetFilterQueryAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
PAGE, rollout.getId(), 10, Long.MAX_VALUE, "controllerid==dyn_action_filter_*", distributionSet.getType()); PAGE, rollout.getId(), 10, Long.MAX_VALUE, "controllerid==dyn_action_filter_*", distributionSet.getType());
assertThat(matching.getNumberOfElements()).isEqualTo(5); assertThat(matching.getNumberOfElements()).isEqualTo(expected.size());
assertThat(matching.stream() assertThat(matching.stream()
.map(Target::getControllerId) .map(Target::getControllerId)
.map(s -> s.substring(targetPrefix.length())) .map(s -> s.substring(targetPrefix.length()))
.map(Integer::parseInt) .map(Integer::parseInt)
.sorted() .sorted()
.toList()).isEqualTo(List.of(0, 1, 2, 5, 6)); .toList()).isEqualTo(expected);
} }
@Test @Test
@@ -1435,6 +1443,7 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
action.setWeight(weight); action.setWeight(weight);
} }
action.setStatus(status); action.setStatus(status);
action.setActive(status != Status.FINISHED && status != Status.ERROR && status != Status.CANCELED);
action.setDistributionSet(distributionSet); action.setDistributionSet(distributionSet);
actionRepository.save(action); actionRepository.save(action);
} }