diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java index c78a44a03..ba982bbb3 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TargetManagement.java @@ -21,12 +21,13 @@ import javax.validation.constraints.NotNull; import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions; import org.eclipse.hawkbit.repository.builder.TargetCreate; import org.eclipse.hawkbit.repository.builder.TargetUpdate; +import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; -import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException; import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException; import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.MetaData; import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.Tag; @@ -34,8 +35,8 @@ import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.repository.model.TargetMetadata; import org.eclipse.hawkbit.repository.model.TargetTag; -import org.eclipse.hawkbit.repository.model.TargetType; import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult; +import org.eclipse.hawkbit.repository.model.TargetType; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -137,7 +138,7 @@ public interface TargetManagement { * if distribution set with given ID does not exist */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET + SpringEvalExpressions.HAS_AUTH_OR - + SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) + + SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) boolean existsByInstalledOrAssignedDistributionSet(long distId); /** @@ -232,9 +233,9 @@ public interface TargetManagement { void deleteByControllerID(@NotEmpty String controllerID); /** - * Finds all targets for all the given parameter {@link TargetFilterQuery} - * and that don't have the specified distribution set in their action - * history. + * Finds all targets for all the given parameter {@link TargetFilterQuery} and + * that don't have the specified distribution set in their action history and + * are compatible with the passed {@link DistributionSetType}. * * @param pageRequest * the pageRequest to enhance the query for paging and sorting @@ -248,13 +249,13 @@ public interface TargetManagement { * if distribution set with given ID does not exist */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - Page findByTargetFilterQueryAndNonDS(@NotNull Pageable pageRequest, long distributionSetId, + Page findByTargetFilterQueryAndNonDSAndCompatible(@NotNull Pageable pageRequest, long distributionSetId, @NotNull String rsqlParam); /** - * Counts all targets for all the given parameter {@link TargetFilterQuery} - * and that don't have the specified distribution set in their action - * history. + * Counts all targets for all the given parameter {@link TargetFilterQuery} and + * that don't have the specified distribution set in their action history and + * are compatible with the passed {@link DistributionSetType}. * * @param distributionSetId * id of the {@link DistributionSet} @@ -266,11 +267,12 @@ public interface TargetManagement { * if distribution set with given ID does not exist */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - long countByRsqlAndNonDS(long distributionSetId, @NotNull String rsqlParam); + long countByRsqlAndNonDSAndCompatible(long distributionSetId, @NotNull String rsqlParam); /** - * Finds all targets for all the given parameter {@link TargetFilterQuery} - * and that are not assigned to one of the {@link RolloutGroup}s + * Finds all targets for all the given parameter {@link TargetFilterQuery} and + * that are not assigned to one of the {@link RolloutGroup}s and are compatible + * with the passed {@link DistributionSetType}. * * @param pageRequest * the pageRequest to enhance the query for paging and sorting @@ -278,24 +280,33 @@ public interface TargetManagement { * the list of {@link RolloutGroup}s * @param rsqlParam * filter definition in RSQL syntax + * @param distributionSetType + * type of the {@link DistributionSet} the targets must be compatible + * with * @return a page of the found {@link Target}s */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - Page findByTargetFilterQueryAndNotInRolloutGroups(@NotNull Pageable pageRequest, - @NotEmpty Collection groups, @NotNull String rsqlParam); + Page findByTargetFilterQueryAndNotInRolloutGroupsAndCompatible(@NotNull Pageable pageRequest, + @NotEmpty Collection groups, @NotNull String rsqlParam, + @NotNull DistributionSetType distributionSetType); /** - * Counts all targets for all the given parameter {@link TargetFilterQuery} - * and that are not assigned to one of the {@link RolloutGroup}s + * Counts all targets for all the given parameter {@link TargetFilterQuery} 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 rsqlParam * filter definition in RSQL syntax + * @param distributionSetType + * type of the {@link DistributionSet} the targets must be compatible + * with * @return count of the found {@link Target}s */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET) - long countByRsqlAndNotInRolloutGroups(@NotEmpty Collection groups, @NotNull String rsqlParam); + long countByRsqlAndNotInRolloutGroupsAndCompatible(@NotEmpty Collection groups, @NotNull String rsqlParam, + @NotNull DistributionSetType distributionSetType); /** * Finds all targets of the provided {@link RolloutGroup} that have no diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/DistributionSetTypeNotInTargetTypeException.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/DistributionSetTypeNotInTargetTypeException.java index d44376900..f711a2303 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/DistributionSetTypeNotInTargetTypeException.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/DistributionSetTypeNotInTargetTypeException.java @@ -8,6 +8,8 @@ */ package org.eclipse.hawkbit.repository.exception; +import java.util.Collection; + import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.TargetType; @@ -24,14 +26,27 @@ public class DistributionSetTypeNotInTargetTypeException extends EntityNotFoundE * Constructor * * @param distributionSetTypeId - * that is not part of given {@link TargetType} - * @param targetTypeId - * of the {@link TargetType} where given - * {@link DistributionSetType} is not part of + * that is not compatible with given {@link TargetType}s + * @param targetTypeIds + * of the {@link TargetType}s where given {@link DistributionSetType} + * is not part of */ public DistributionSetTypeNotInTargetTypeException(final Long distributionSetTypeId, - final Long targetTypeId) { - super(DistributionSetType.class.getSimpleName() + " with id {" + distributionSetTypeId + "} is not part of " - + TargetType.class.getSimpleName() + " with id {" + targetTypeId + "}."); + final Collection targetTypeIds) { + super("DistributionSetType " + distributionSetTypeId + " is not compatible to TargetTypes " + targetTypeIds); + } + + /** + * Constructor + * + * @param distributionSetTypeIds + * that are not compatible with given {@link TargetType} + * @param targetTypeId + * of the {@link TargetType} where given {@link DistributionSetType}s + * are not part of + */ + public DistributionSetTypeNotInTargetTypeException(final Collection distributionSetTypeIds, + final Long targetTypeId) { + super("DistributionSetTypes " + distributionSetTypeIds + " are not compatible to TargetTypes " + targetTypeId); } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index 90489f6d6..706989b6a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -42,6 +42,7 @@ import org.eclipse.hawkbit.repository.RepositoryProperties; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException; +import org.eclipse.hawkbit.repository.exception.DistributionSetTypeNotInTargetTypeException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException; import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException; @@ -71,6 +72,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.Cancelat import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.TargetType; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder; @@ -229,10 +231,60 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl deploymentRequests = deploymentRequests.stream().distinct().collect(Collectors.toList()); checkIfRequiresMultiAssignment(deploymentRequests); } + checkForTargetTypeCompatibility(deploymentRequests); checkQuotaForAssignment(deploymentRequests); return deploymentRequests; } + private void checkForTargetTypeCompatibility(final List deploymentRequests) { + final List controllerIds = deploymentRequests.stream().map(DeploymentRequest::getControllerId) + .distinct().collect(Collectors.toList()); + final List distSetIds = deploymentRequests.stream().map(DeploymentRequest::getDistributionSetId) + .distinct().collect(Collectors.toList()); + + if (controllerIds.size() > 1 && distSetIds.size() > 1) { + throw new IllegalStateException( + "Assigning multiple Targets to multiple Distribution Sets simultaneously is not allowed!"); + } + + if (distSetIds.size() == 1) { + checkCompatibilityForSingleDsAssignment(distSetIds.iterator().next(), controllerIds); + } else { + checkCompatibilityForMultiDsAssignment(controllerIds.iterator().next(), distSetIds); + } + } + + private void checkCompatibilityForSingleDsAssignment(final Long distSetId, final List controllerIds) { + final DistributionSetType distSetType = distributionSetManagement.getValidAndComplete(distSetId).getType(); + final Set incompatibleTargetTypes = Lists.partition(controllerIds, Constants.MAX_ENTRIES_IN_STATEMENT) + .stream() + .map(ids -> targetRepository.findAll(TargetSpecifications.hasControllerIdIn(ids) + .and(TargetSpecifications.notCompatibleWithDistributionSetType(distSetType.getId())))) + .flatMap(List::stream).map(Target::getTargetType).map(TargetType::getId).collect(Collectors.toSet()); + + if (!incompatibleTargetTypes.isEmpty()) { + throw new DistributionSetTypeNotInTargetTypeException(distSetType.getId(), incompatibleTargetTypes); + } + } + + private void checkCompatibilityForMultiDsAssignment(final String controllerId, final List distSetIds) { + final Target target = targetRepository.findOne(TargetSpecifications.hasControllerId(controllerId)) + .orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId)); + + if (target.getTargetType() != null) { + // we assume that list of assigned DS is less than MAX_ENTRIES_IN_STATEMENT + final Set incompatibleDistSetTypes = distributionSetManagement.get(distSetIds).stream() + .map(DistributionSet::getType).collect(Collectors.toSet()); + incompatibleDistSetTypes.removeAll(target.getTargetType().getCompatibleDistributionSetTypes()); + + if (!incompatibleDistSetTypes.isEmpty()) { + final Set distSetTypeIds = incompatibleDistSetTypes.stream().map(DistributionSetType::getId) + .collect(Collectors.toSet()); + throw new DistributionSetTypeNotInTargetTypeException(distSetTypeIds, target.getTargetType().getId()); + } + } + } + private static Map> convertRequest( final Collection deploymentRequests) { return deploymentRequests.stream().collect(Collectors.groupingBy(DeploymentRequest::getDistributionSetId, @@ -256,8 +308,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl } /** - * method assigns the {@link DistributionSet} to all {@link Target}s by - * their IDs with a specific {@link ActionType} and {@code forcetime}. + * method assigns the {@link DistributionSet} to all {@link Target}s by their + * IDs with a specific {@link ActionType} and {@code forcetime}. * * * In case the update was executed offline (i.e. not managed by hawkBit) the @@ -265,8 +317,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl * A. it ignores targets completely that are in * {@link TargetUpdateStatus#PENDING}.
* B. it created completed actions.
- * C. sets both installed and assigned DS on the target and switches the - * status to {@link TargetUpdateStatus#IN_SYNC}
+ * C. sets both installed and assigned DS on the target and switches the status + * to {@link TargetUpdateStatus#IN_SYNC}
* D. does not send a {@link TargetAssignDistributionSetEvent}.
* * @param initiatedBy @@ -281,9 +333,9 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl * the assignment strategy (online /offline) * @return the assignment result * - * @throw IncompleteDistributionSetException if mandatory - * {@link SoftwareModuleType} are not assigned as define by the - * {@link DistributionSetType}. + * @throws IncompleteDistributionSetException + * if mandatory {@link SoftwareModuleType} are not assigned as + * define by the {@link DistributionSetType}. */ private DistributionSetAssignmentResult assignDistributionSetToTargets(final String initiatedBy, final Long dsID, final Collection targetsWithActionType, final String actionMessage, @@ -351,9 +403,9 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl } /** - * split tIDs length into max entries in-statement because many database - * have constraint of max entries in in-statements e.g. Oracle with maximum - * 1000 elements, so we need to split the entries here and execute multiple + * split tIDs length into max entries in-statement because many database have + * constraint of max entries in in-statements e.g. Oracle with maximum 1000 + * elements, so we need to split the entries here and execute multiple * statements */ private static List> getTargetEntitiesAsChunks(final List targetEntities) { @@ -813,10 +865,10 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl return 0; } /* - * We use a native query here because Spring JPA does not support to - * specify a LIMIT clause on a DELETE statement. However, for this - * specific use case (action cleanup), we must specify a row limit to - * reduce the overall load on the database. + * We use a native query here because Spring JPA does not support to specify a + * LIMIT clause on a DELETE statement. However, for this specific use case + * (action cleanup), we must specify a row limit to reduce the overall load on + * the database. */ final int statusCount = status.size(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java index 8ab55e4e6..febb0c263 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutExecutor.java @@ -530,7 +530,8 @@ public class JpaRolloutExecutor implements RolloutExecutor { final long targetsInGroupFilter = DeploymentHelper.runInNewTransaction(txManager, "countAllTargetsByTargetFilterQueryAndNotInRolloutGroups", - count -> targetManagement.countByRsqlAndNotInRolloutGroups(readyGroups, groupTargetFilter)); + count -> targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatible(readyGroups, groupTargetFilter, + rollout.getDistributionSet().getType())); final long expectedInGroup = Math .round((double) (group.getTargetPercentage() / 100) * (double) targetsInGroupFilter); final long currentlyInGroup = DeploymentHelper.runInNewTransaction(txManager, @@ -574,8 +575,8 @@ public class JpaRolloutExecutor implements RolloutExecutor { final PageRequest pageRequest = PageRequest.of(0, Math.toIntExact(limit)); final List readyGroups = RolloutHelper.getGroupsByStatusIncludingGroup(rollout.getRolloutGroups(), RolloutGroupStatus.READY, group); - final Page targets = targetManagement.findByTargetFilterQueryAndNotInRolloutGroups(pageRequest, - readyGroups, targetFilter); + final Page targets = targetManagement.findByTargetFilterQueryAndNotInRolloutGroupsAndCompatible( + pageRequest, readyGroups, targetFilter, rollout.getDistributionSet().getType()); createAssignmentOfTargetsToGroup(targets, group); @@ -584,8 +585,8 @@ public class JpaRolloutExecutor implements RolloutExecutor { } /** - * Schedules a group of the rollout. Scheduled Actions are created to - * achieve this. The creation of those Actions is allowed to fail. + * Schedules a group of the rollout. Scheduled Actions are created to achieve + * this. The creation of those Actions is allowed to fail. */ private boolean scheduleRolloutGroup(final JpaRollout rollout, final JpaRolloutGroup group) { final long targetsInGroup = rolloutTargetGroupRepository.countByRolloutGroup(group); @@ -648,8 +649,8 @@ public class JpaRolloutExecutor implements RolloutExecutor { /** * Creates an action entry into the action repository. In case of existing - * scheduled actions the scheduled actions gets canceled. A scheduled action - * is created in-active. + * scheduled actions the scheduled actions gets canceled. A scheduled action is + * created in-active. */ private void createScheduledAction(final Collection targets, final DistributionSet distributionSet, final ActionType actionType, final Long forcedTime, final Rollout rollout, diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java index 75b557ed4..7c5ef5d6c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTargetManagement.java @@ -27,6 +27,7 @@ import javax.persistence.criteria.MapJoin; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.FilterParams; import org.eclipse.hawkbit.repository.QuotaManagement; import org.eclipse.hawkbit.repository.TargetFields; @@ -57,6 +58,7 @@ import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder; import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.MetaData; import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.Target; @@ -84,8 +86,6 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; -import com.google.common.collect.Lists; - /** * JPA implementation of {@link TargetManagement}. * @@ -96,6 +96,8 @@ public class JpaTargetManagement implements TargetManagement { private final EntityManager entityManager; + private final DistributionSetManagement distributionSetManagement; + private final QuotaManagement quotaManagement; private final TargetRepository targetRepository; @@ -106,8 +108,6 @@ public class JpaTargetManagement implements TargetManagement { private final RolloutGroupRepository rolloutGroupRepository; - private final DistributionSetRepository distributionSetRepository; - private final TargetFilterQueryRepository targetFilterQueryRepository; private final TargetTagRepository targetTagRepository; @@ -122,22 +122,22 @@ public class JpaTargetManagement implements TargetManagement { private final Database database; - public JpaTargetManagement(final EntityManager entityManager, final QuotaManagement quotaManagement, + public JpaTargetManagement(final EntityManager entityManager, + final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement, final TargetRepository targetRepository, final TargetTypeRepository targetTypeRepository, final TargetMetadataRepository targetMetadataRepository, final RolloutGroupRepository rolloutGroupRepository, - final DistributionSetRepository distributionSetRepository, final TargetFilterQueryRepository targetFilterQueryRepository, final TargetTagRepository targetTagRepository, final EventPublisherHolder eventPublisherHolder, final TenantAware tenantAware, final AfterTransactionCommitExecutor afterCommit, final VirtualPropertyReplacer virtualPropertyReplacer, final Database database) { this.entityManager = entityManager; + this.distributionSetManagement = distributionSetManagement; this.quotaManagement = quotaManagement; this.targetRepository = targetRepository; this.targetTypeRepository = targetTypeRepository; this.targetMetadataRepository = targetMetadataRepository; this.rolloutGroupRepository = rolloutGroupRepository; - this.distributionSetRepository = distributionSetRepository; this.targetFilterQueryRepository = targetFilterQueryRepository; this.targetTagRepository = targetTagRepository; this.eventPublisherHolder = eventPublisherHolder; @@ -383,17 +383,16 @@ public class JpaTargetManagement implements TargetManagement { @Override public Page findByAssignedDistributionSet(final Pageable pageReq, final long distributionSetID) { - throwEntityNotFoundIfDsDoesNotExist(distributionSetID); + final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetID); - return convertPage( - targetRepository.findAll(TargetSpecifications.hasAssignedDistributionSet(distributionSetID), pageReq), - pageReq); + return convertPage(targetRepository + .findAll(TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId()), pageReq), pageReq); } @Override public Page findByAssignedDistributionSetAndRsql(final Pageable pageReq, final long distributionSetID, final String rsqlParam) { - throwEntityNotFoundIfDsDoesNotExist(distributionSetID); + final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetID); final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, TargetFields.class, virtualPropertyReplacer, database); @@ -401,18 +400,12 @@ public class JpaTargetManagement implements TargetManagement { return convertPage( targetRepository .findAll((Specification) (root, query, - cb) -> cb.and(TargetSpecifications.hasAssignedDistributionSet(distributionSetID) + cb) -> cb.and(TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId()) .toPredicate(root, query, cb), spec.toPredicate(root, query, cb)), pageReq), pageReq); } - private void throwEntityNotFoundIfDsDoesNotExist(final Long distributionSetID) { - if (!distributionSetRepository.existsById(distributionSetID)) { - throw new EntityNotFoundException(DistributionSet.class, distributionSetID); - } - } - public static Page convertPage(final Page findAll, final Pageable pageable) { return new PageImpl<>(Collections.unmodifiableList(findAll.getContent()), pageable, findAll.getTotalElements()); } @@ -423,16 +416,15 @@ public class JpaTargetManagement implements TargetManagement { @Override public Page findByInstalledDistributionSet(final Pageable pageReq, final long distributionSetID) { - throwEntityNotFoundIfDsDoesNotExist(distributionSetID); - return convertPage( - targetRepository.findAll(TargetSpecifications.hasInstalledDistributionSet(distributionSetID), pageReq), - pageReq); + final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetID); + return convertPage(targetRepository + .findAll(TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId()), pageReq), pageReq); } @Override public Page findByInstalledDistributionSetAndRsql(final Pageable pageable, final long distributionSetId, final String rsqlParam) { - throwEntityNotFoundIfDsDoesNotExist(distributionSetId); + final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException(distributionSetId); final Specification spec = RSQLUtility.buildRsqlSpecification(rsqlParam, TargetFields.class, virtualPropertyReplacer, database); @@ -440,7 +432,7 @@ public class JpaTargetManagement implements TargetManagement { return convertPage( targetRepository .findAll((Specification) (root, query, - cb) -> cb.and(TargetSpecifications.hasInstalledDistributionSet(distributionSetId) + cb) -> cb.and(TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId()) .toPredicate(root, query, cb), spec.toPredicate(root, query, cb)), pageable), pageable); @@ -476,10 +468,10 @@ public class JpaTargetManagement implements TargetManagement { specList.add(TargetSpecifications.isOverdue(TimestampCalculator.calculateOverdueTimestamp())); } if (filterParams.getFilterByDistributionId() != null) { - throwEntityNotFoundIfDsDoesNotExist(filterParams.getFilterByDistributionId()); + final DistributionSet validDistSet = distributionSetManagement + .getOrElseThrowException(filterParams.getFilterByDistributionId()); - specList.add(TargetSpecifications - .hasInstalledOrAssignedDistributionSet(filterParams.getFilterByDistributionId())); + specList.add(TargetSpecifications.hasInstalledOrAssignedDistributionSet(validDistSet.getId())); } if (!StringUtils.isEmpty(filterParams.getFilterBySearchText())) { specList.add(TargetSpecifications @@ -669,51 +661,53 @@ public class JpaTargetManagement implements TargetManagement { @Override public long countByAssignedDistributionSet(final long distId) { - throwEntityNotFoundIfDsDoesNotExist(distId); + final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distId)); - return targetRepository.count(TargetSpecifications.hasAssignedDistributionSet(distId)); + return targetRepository.count(TargetSpecifications.hasAssignedDistributionSet(validDistSet.getId())); } @Override public long countByInstalledDistributionSet(final long distId) { - throwEntityNotFoundIfDsDoesNotExist(distId); + final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distId)); - return targetRepository.count(TargetSpecifications.hasInstalledDistributionSet(distId)); + return targetRepository.count(TargetSpecifications.hasInstalledDistributionSet(validDistSet.getId())); } @Override public boolean existsByInstalledOrAssignedDistributionSet(final long distId) { - throwEntityNotFoundIfDsDoesNotExist(distId); + final DistributionSet validDistSet = distributionSetManagement.getOrElseThrowException((distId)); - return targetRepository.exists(TargetSpecifications.hasInstalledOrAssignedDistributionSet(distId)); + return targetRepository + .exists(TargetSpecifications.hasInstalledOrAssignedDistributionSet(validDistSet.getId())); } @Override - public Page findByTargetFilterQueryAndNonDS(final Pageable pageRequest, final long distributionSetId, - final String targetFilterQuery) { - throwEntityNotFoundIfDsDoesNotExist(distributionSetId); + public Page findByTargetFilterQueryAndNonDSAndCompatible(final Pageable pageRequest, + final long distributionSetId, final String targetFilterQuery) { + final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId); + final Long distSetTypeId = jpaDistributionSet.getType().getId(); final Specification spec = RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database); + final Specification dsNotInActions = TargetSpecifications + .hasNotDistributionSetInActions(distributionSetId); + final Specification isCompatibleWithDsType = TargetSpecifications + .isCompatibleWithDistributionSetType(distSetTypeId); - return findTargetsBySpec( - (root, cq, - cb) -> cb.and(spec.toPredicate(root, cq, cb), TargetSpecifications - .hasNotDistributionSetInActions(distributionSetId).toPredicate(root, cq, cb)), - pageRequest); - + return findTargetsBySpec(spec.and(dsNotInActions).and(isCompatibleWithDsType), pageRequest); } @Override - public Page findByTargetFilterQueryAndNotInRolloutGroups(final Pageable pageRequest, - final Collection groups, final String targetFilterQuery) { + public Page findByTargetFilterQueryAndNotInRolloutGroupsAndCompatible(final Pageable pageRequest, + final Collection groups, final String targetFilterQuery, final DistributionSetType dsType) { final Specification spec = RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database); + final Specification notInRolloutGroups = TargetSpecifications.isNotInRolloutGroups(groups); + final Specification isCompatibleWithDsType = TargetSpecifications + .isCompatibleWithDistributionSetType(dsType.getId()); - return findTargetsBySpec((root, cq, cb) -> cb.and(spec.toPredicate(root, cq, cb), - TargetSpecifications.isNotInRolloutGroups(groups).toPredicate(root, cq, cb)), pageRequest); - + return findTargetsBySpec((spec.and(notInRolloutGroups).and(isCompatibleWithDsType)), pageRequest); } @Override @@ -728,24 +722,27 @@ public class JpaTargetManagement implements TargetManagement { } @Override - public long countByRsqlAndNotInRolloutGroups(final Collection groups, final String targetFilterQuery) { + public long countByRsqlAndNotInRolloutGroupsAndCompatible(final Collection groups, + final String targetFilterQuery, final DistributionSetType dsType) { final Specification spec = RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database); final List> specList = Arrays.asList(spec, - TargetSpecifications.isNotInRolloutGroups(groups)); + TargetSpecifications.isNotInRolloutGroups(groups), + TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId())); return countByCriteriaAPI(specList); } @Override - public long countByRsqlAndNonDS(final long distributionSetId, final String targetFilterQuery) { - throwEntityNotFoundIfDsDoesNotExist(distributionSetId); + public long countByRsqlAndNonDSAndCompatible(final long distributionSetId, final String targetFilterQuery) { + final DistributionSet jpaDistributionSet = distributionSetManagement.getOrElseThrowException(distributionSetId); + final Long distSetTypeId = jpaDistributionSet.getType().getId(); final Specification spec = RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer, database); - final List> specList = Lists.newArrayListWithExpectedSize(2); - specList.add(spec); - specList.add(TargetSpecifications.hasNotDistributionSetInActions(distributionSetId)); + final List> specList = Arrays.asList(spec, + TargetSpecifications.hasNotDistributionSetInActions(distributionSetId), + TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId)); return countByCriteriaAPI(specList); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java index 9fbdfee4b..6d0bf33bc 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/RepositoryApplicationConfiguration.java @@ -244,8 +244,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { /** * @param dsTypeManagement - * for loading - * {@link TargetType#getCompatibleDistributionSetTypes()} + * for loading {@link TargetType#getCompatibleDistributionSetTypes()} * @return TargetTypeBuilder bean */ @Bean @@ -261,9 +260,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { /** * @param softwareManagement - * for loading - * {@link DistributionSetType#getMandatoryModuleTypes()} and - * {@link DistributionSetType#getOptionalModuleTypes()} + * for loading {@link DistributionSetType#getMandatoryModuleTypes()} + * and {@link DistributionSetType#getOptionalModuleTypes()} * @return DistributionSetTypeBuilder bean */ @Bean @@ -305,8 +303,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { /** * @return the {@link SystemSecurityContext} singleton bean which make it - * accessible in beans which cannot access the service directly, - * e.g. JPA entities. + * accessible in beans which cannot access the service directly, e.g. + * JPA entities. */ @Bean SystemSecurityContextHolder systemSecurityContextHolder() { @@ -314,9 +312,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { } /** - * @return the {@link TenantConfigurationManagement} singleton bean which - * make it accessible in beans which cannot access the service - * directly, e.g. JPA entities. + * @return the {@link TenantConfigurationManagement} singleton bean which make + * it accessible in beans which cannot access the service directly, e.g. + * JPA entities. */ @Bean TenantConfigurationManagementHolder tenantConfigurationManagementHolder() { @@ -325,9 +323,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { /** * @return the {@link SystemManagementHolder} singleton bean which holds the - * current {@link SystemManagement} service and make it accessible - * in beans which cannot access the service directly, e.g. JPA - * entities. + * current {@link SystemManagement} service and make it accessible in + * beans which cannot access the service directly, e.g. JPA entities. */ @Bean SystemManagementHolder systemManagementHolder() { @@ -335,10 +332,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { } /** - * @return the {@link TenantAwareHolder} singleton bean which holds the - * current {@link TenantAware} service and make it accessible in - * beans which cannot access the service directly, e.g. JPA - * entities. + * @return the {@link TenantAwareHolder} singleton bean which holds the current + * {@link TenantAware} service and make it accessible in beans which + * cannot access the service directly, e.g. JPA entities. */ @Bean TenantAwareHolder tenantAwareHolder() { @@ -346,10 +342,9 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { } /** - * @return the {@link SecurityTokenGeneratorHolder} singleton bean which - * holds the current {@link SecurityTokenGenerator} service and make - * it accessible in beans which cannot access the service via - * injection + * @return the {@link SecurityTokenGeneratorHolder} singleton bean which holds + * the current {@link SecurityTokenGenerator} service and make it + * accessible in beans which cannot access the service via injection */ @Bean SecurityTokenGeneratorHolder securityTokenGeneratorHolder() { @@ -532,25 +527,24 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration { } /** - * {@link JpaTenantConfigurationManagement} bean. + * {@link JpaTargetManagement} bean. * - * @return a new {@link TenantConfigurationManagement} + * @return a new {@link JpaTargetManagement} */ @Bean @ConditionalOnMissingBean TargetManagement targetManagement(final EntityManager entityManager, final QuotaManagement quotaManagement, final TargetRepository targetRepository, final TargetMetadataRepository targetMetadataRepository, final RolloutGroupRepository rolloutGroupRepository, - final DistributionSetRepository distributionSetRepository, final TargetFilterQueryRepository targetFilterQueryRepository, final TargetTypeRepository targetTypeRepository, final TargetTagRepository targetTagRepository, final EventPublisherHolder eventPublisherHolder, final TenantAware tenantAware, final AfterTransactionCommitExecutor afterCommit, final VirtualPropertyReplacer virtualPropertyReplacer, - final JpaProperties properties) { - return new JpaTargetManagement(entityManager, quotaManagement, targetRepository, targetTypeRepository, - targetMetadataRepository, rolloutGroupRepository, distributionSetRepository, - targetFilterQueryRepository, targetTagRepository, eventPublisherHolder, tenantAware, afterCommit, - virtualPropertyReplacer, properties.getDatabase()); + final JpaProperties properties, final DistributionSetManagement distributionSetManagement) { + return new JpaTargetManagement(entityManager, distributionSetManagement, quotaManagement, targetRepository, + targetTypeRepository, targetMetadataRepository, rolloutGroupRepository, targetFilterQueryRepository, + targetTagRepository, eventPublisherHolder, tenantAware, afterCommit, virtualPropertyReplacer, + properties.getDatabase()); } /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java index ec79a7047..b110988a4 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignChecker.java @@ -84,14 +84,15 @@ public class AutoAssignChecker extends AbstractAutoAssignExecutor { do { final List controllerIds = targetManagement - .findByTargetFilterQueryAndNonDS(PageRequest.of(0, Constants.MAX_ENTRIES_IN_STATEMENT), + .findByTargetFilterQueryAndNonDSAndCompatible( + PageRequest.of(0, Constants.MAX_ENTRIES_IN_STATEMENT), targetFilterQuery.getAutoAssignDistributionSet().getId(), targetFilterQuery.getQuery()) .getContent().stream().map(Target::getControllerId).collect(Collectors.toList()); count = runTransactionalAssignment(targetFilterQuery, controllerIds); } while (count == Constants.MAX_ENTRIES_IN_STATEMENT); - } catch (PersistenceException | AbstractServerRtException e) { + } catch (final PersistenceException | AbstractServerRtException e) { LOGGER.error("Error during auto assign check of target filter query " + targetFilterQuery.getId(), e); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java index c0ebed849..9f104113d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/specifications/TargetSpecifications.java @@ -13,6 +13,7 @@ import java.util.Collection; import java.util.List; import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.Join; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.ListJoin; import javax.persistence.criteria.MapJoin; @@ -25,18 +26,24 @@ import javax.validation.constraints.NotNull; import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction_; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; +import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType; +import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType_; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_; import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup_; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag_; +import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType; +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.DistributionSet; +import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetTag; +import org.eclipse.hawkbit.repository.model.TargetType; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.springframework.data.jpa.domain.Specification; @@ -150,7 +157,7 @@ public final class TargetSpecifications { * * @param updateStatus * to be filtered on - * + * * @return the {@link Target} {@link Specification} */ public static Specification hasTargetUpdateStatus(final Collection updateStatus) { @@ -176,7 +183,7 @@ public final class TargetSpecifications { * * @param updateStatus * to be filtered on - * + * * @return the {@link Target} {@link Specification} */ public static Specification notEqualToTargetUpdateStatus(final TargetUpdateStatus updateStatus) { @@ -185,15 +192,14 @@ public final class TargetSpecifications { /** * {@link Specification} for retrieving {@link Target}s that are overdue. A - * target is overdue if it did not respond during the configured - * intervals:
+ * target is overdue if it did not respond during the configured intervals:
* poll_itvl + overdue_itvl * * @param overdueTimestamp * the calculated timestamp to compare with the last respond of a * target (lastTargetQuery).
- * The overdueTimestamp has to be calculated with - * the following expression:
+ * The overdueTimestamp has to be calculated with the + * following expression:
* overdueTimestamp = nowTimestamp - poll_itvl - * overdue_itvl * @@ -205,8 +211,8 @@ public final class TargetSpecifications { } /** - * {@link Specification} for retrieving {@link Target}s by "like - * controllerId or like name or like description". + * {@link Specification} for retrieving {@link Target}s by "like controllerId or + * like name or like description". * * @param searchText * to be filtered on @@ -240,8 +246,8 @@ public final class TargetSpecifications { } /** - * {@link Specification} for retrieving {@link Target}s by "like - * controllerId or like name or like description or like attribute value". + * {@link Specification} for retrieving {@link Target}s by "like controllerId or + * like name or like description or like attribute value". * * @param searchText * to be filtered on @@ -252,8 +258,7 @@ public final class TargetSpecifications { } /** - * {@link Specification} for retrieving {@link Target}s by "like - * controllerId". + * {@link Specification} for retrieving {@link Target}s by "like controllerId". * * @param distributionId * to be filtered on @@ -264,8 +269,8 @@ public final class TargetSpecifications { } /** - * Finds all targets by given {@link Target#getControllerId()}s and which - * are not yet assigned to given {@link DistributionSet}. + * Finds all targets by given {@link Target#getControllerId()}s and which are + * not yet assigned to given {@link DistributionSet}. * * @param tIDs * to search for. @@ -298,8 +303,8 @@ public final class TargetSpecifications { } /** - * {@link Specification} for retrieving {@link Target}s by "has no tag - * names"or "has at least on of the given tag names". + * {@link Specification} for retrieving {@link Target}s by "has no tag names"or + * "has at least on of the given tag names". * * @param tagNames * to be filtered on @@ -341,8 +346,8 @@ public final class TargetSpecifications { } /** - * {@link Specification} for retrieving {@link Target}s by assigned - * distribution set. + * {@link Specification} for retrieving {@link Target}s by assigned distribution + * set. * * @param distributionSetId * the ID of the distribution set which must be assigned @@ -372,6 +377,57 @@ public final class TargetSpecifications { }; } + /** + * {@link Specification} for retrieving {@link Target}s that are 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 {@link DistributionSetType} + * + * @param distributionSetTypeId + * the ID of the distribution set type which must be compatible + * @return the {@link Target} {@link Specification} + */ + public static Specification isCompatibleWithDistributionSetType(final Long distributionSetTypeId) { + return (targetRoot, query, cb) -> { + // Since the targetRoot is changed by joining we need to get the + // isNull predicate first + final Predicate targetTypeIsNull = targetRoot.get(JpaTarget_.targetType).isNull(); + + return cb.or(targetTypeIsNull, getDistSetTypeEqualPredicate(targetRoot, cb, distributionSetTypeId)); + }; + } + + /** + * {@link Specification} for retrieving {@link Target}s that are NOT 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 {@link DistributionSetType} + * + * @param distributionSetTypeId + * the ID of the distribution set type which must be incompatible + * @return the {@link Target} {@link Specification} + */ + public static Specification notCompatibleWithDistributionSetType(final Long distributionSetTypeId) { + return (targetRoot, query, cb) -> { + // Since the targetRoot is changed by joining we need to get the + // isNotNull predicate first + final Predicate targetTypeNotNull = targetRoot.get(JpaTarget_.targetType).isNotNull(); + + return cb.and(targetTypeNotNull, + cb.isNull(getDistSetTypeEqualPredicate(targetRoot, cb, distributionSetTypeId))); + }; + } + + private static Predicate getDistSetTypeEqualPredicate(final Root root, final CriteriaBuilder cb, + final Long dsTypeId) { + final Join targetTypeJoin = root.join(JpaTarget_.targetType, JoinType.LEFT); + targetTypeJoin.fetch(JpaTargetType_.distributionSetTypes); + final SetJoin dsTypeTargetTypeJoin = targetTypeJoin + .join(JpaTargetType_.distributionSetTypes, JoinType.LEFT); + + return cb.equal(dsTypeTargetTypeJoin.get(JpaDistributionSetType_.id), dsTypeId); + } + /** * {@link Specification} for retrieving {@link Target}s that are in a given * {@link RolloutGroup} @@ -423,8 +479,8 @@ public final class TargetSpecifications { } /** - * {@link Specification} for retrieving {@link Target}s that have no Action - * of the {@link RolloutGroup}. + * {@link Specification} for retrieving {@link Target}s that have no Action of + * the {@link RolloutGroup}. * * @param group * the {@link RolloutGroup} @@ -445,8 +501,8 @@ public final class TargetSpecifications { } /** - * {@link Specification} for retrieving {@link Target}s by assigned - * distribution set. + * {@link Specification} for retrieving {@link Target}s by assigned distribution + * set. * * @param distributionSetId * the ID of the distribution set which must be assigned @@ -471,4 +527,14 @@ public final class TargetSpecifications { return cb.equal(tags.get(JpaTargetTag_.id), tagId); }; } + + /** + * {@link Specification} for retrieving {@link Target}s that have a + * {@link org.eclipse.hawkbit.repository.model.TargetType} assigned + * + * @return the {@link Target} {@link Specification} + */ + public static Specification hasTargetType() { + return (targetRoot, query, cb) -> cb.isNotNull(targetRoot.get(JpaTarget_.targetType)); + } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java index 8b0563fdb..b9390bcd4 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java @@ -19,6 +19,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map.Entry; +import java.util.Optional; import java.util.stream.Collectors; import javax.validation.ConstraintViolationException; @@ -40,6 +41,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TenantConfigurationCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TenantConfigurationUpdatedEvent; import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; +import org.eclipse.hawkbit.repository.exception.DistributionSetTypeNotInTargetTypeException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException; import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException; @@ -62,6 +64,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.TargetType; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.test.matcher.Expect; import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents; @@ -69,6 +72,7 @@ import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.T import org.junit.jupiter.api.Test; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort.Direction; @@ -87,25 +91,25 @@ import io.qameta.allure.Story; */ @Feature("Component Tests - Repository") @Story("Deployment Management") -public class DeploymentManagementTest extends AbstractJpaIntegrationTest { +class DeploymentManagementTest extends AbstractJpaIntegrationTest { private static final boolean STATE_ACTIVE = true; private static final boolean STATE_INACTIVE = false; @Test - @Description("Verifies that management get access react as specfied on calls for non existing entities by means " + @Description("Verifies that management get access react as specified on calls for non existing entities by means " + "of Optional not present.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) }) - public void nonExistingEntityAccessReturnsNotPresent() { + void nonExistingEntityAccessReturnsNotPresent() { assertThat(deploymentManagement.findAction(1234L)).isNotPresent(); assertThat(deploymentManagement.findActionWithDetails(NOT_EXIST_IDL)).isNotPresent(); } @Test - @Description("Verifies that management queries react as specfied on calls for non existing entities " + @Description("Verifies that management queries react as specified on calls for non existing entities " + " by means of throwing EntityNotFoundException.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1) }) - public void entityQueriesReferringToNotExistingEntitiesThrowsException() { + void entityQueriesReferringToNotExistingEntitiesThrowsException() { final Target target = testdataFactory.createTarget(); final String dsName = "DistributionSet"; @@ -128,7 +132,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Test verifies that the repistory retrieves the action including all defined (lazy) details.") - public void findActionWithLazyDetails() { + void findActionWithLazyDetails() { final DistributionSet testDs = testdataFactory.createDistributionSet("TestDs", "1.0", new ArrayList()); final List testTarget = testdataFactory.createTargets(1); @@ -145,7 +149,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Test verifies that actions of a target are found by using id-based search.") - public void findActionByTargetId() { + void findActionByTargetId() { final DistributionSet testDs = testdataFactory.createDistributionSet("TestDs", "1.0", new ArrayList()); final List testTarget = testdataFactory.createTargets(1); @@ -163,7 +167,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Test verifies that the 'max actions per target' quota is enforced.") - public void assertMaxActionsPerTargetQuotaIsEnforced() { + void assertMaxActionsPerTargetQuotaIsEnforced() { final int maxActions = quotaManagement.getMaxActionsPerTarget(); final Target testTarget = testdataFactory.createTarget(); @@ -181,7 +185,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Test @Description("An assignment request with more assignments than allowed by 'maxTargetDistributionSetAssignmentsPerManualAssignment' quota throws an exception.") - public void assignmentRequestThatIsTooLarge() { + void assignmentRequestThatIsTooLarge() { final int maxActions = quotaManagement.getMaxTargetDistributionSetAssignmentsPerManualAssignment(); final DistributionSet ds1 = testdataFactory.createDistributionSet("1"); final DistributionSet ds2 = testdataFactory.createDistributionSet("2"); @@ -196,7 +200,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Test verifies that action-states of an action are found by using id-based search.") - public void findActionStatusByActionId() { + void findActionStatusByActionId() { final DistributionSet testDs = testdataFactory.createDistributionSet("TestDs", "1.0", Collections.emptyList()); final List testTarget = testdataFactory.createTargets(1); // one action with one action status is generated @@ -214,7 +218,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Test verifies that messages of an action-status are found by using id-based search.") - public void findMessagesByActionStatusId() { + void findMessagesByActionStatusId() { final DistributionSet testDs = testdataFactory.createDistributionSet("TestDs", "1.0", new ArrayList()); final List testTarget = testdataFactory.createTargets(1); @@ -240,7 +244,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Ensures that tag to distribution set assignment that does not exist will cause EntityNotFoundException.") - public void assignDistributionSetToTagThatDoesNotExistThrowsException() { + void assignDistributionSetToTagThatDoesNotExistThrowsException() { final List assignDS = Lists.newArrayListWithExpectedSize(5); for (int i = 0; i < 4; i++) { assignDS.add(testdataFactory.createDistributionSet("DS" + i, "1.0", Collections.emptyList()).getId()); @@ -265,7 +269,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = ActionUpdatedEvent.class, count = Constants.MAX_ENTRIES_IN_STATEMENT + 10), @Expect(type = DistributionSetCreatedEvent.class, count = 2), @Expect(type = SoftwareModuleCreatedEvent.class, count = 6) }) - public void multiAssigmentHistoryOverMultiplePagesResultsInTwoActiveAction() { + void multiAssigmentHistoryOverMultiplePagesResultsInTwoActiveAction() { final DistributionSet cancelDs = testdataFactory.createDistributionSet("Canceled DS", "1.0", Collections.emptyList()); @@ -275,7 +279,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { final List targets = testdataFactory.createTargets(Constants.MAX_ENTRIES_IN_STATEMENT + 10); - assertThat(deploymentManagement.countActionsAll()).isEqualTo(0); + assertThat(deploymentManagement.countActionsAll()).isZero(); assignDistributionSet(cancelDs, targets).getAssignedEntity(); assertThat(deploymentManagement.countActionsAll()).isEqualTo(Constants.MAX_ENTRIES_IN_STATEMENT + 10); @@ -287,7 +291,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Description("Cancels multiple active actions on a target. Expected behaviour is that with two active " + "actions after canceling the second active action the first one is still running as it is not touched by the cancelation. After canceling the first one " + "also the target goes back to IN_SYNC as no open action is left.") - public void manualCancelWithMultipleAssignmentsCancelLastOneFirst() { + void manualCancelWithMultipleAssignmentsCancelLastOneFirst() { final Action action = prepareFinishedUpdate("4712", "installed", true); final Target target = action.getTarget(); final DistributionSet dsFirst = testdataFactory.createDistributionSet("", true); @@ -312,7 +316,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { controllerManagement.addCancelActionStatus( entityFactory.actionStatus().create(secondAction.getId()).status(Status.CANCELED)); assertThat(actionStatusRepository.findAll()).as("wrong size of actions status").hasSize(7); - assertThat(deploymentManagement.getAssignedDistributionSet("4712").get()).as("wrong ds").isEqualTo(dsFirst); + assertThat(deploymentManagement.getAssignedDistributionSet("4712")).as("wrong ds").contains(dsFirst); assertThat(targetManagement.getByControllerID("4712").get().getUpdateStatus()).as("wrong update status") .isEqualTo(TargetUpdateStatus.PENDING); @@ -323,8 +327,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { controllerManagement.addCancelActionStatus( entityFactory.actionStatus().create(firstAction.getId()).status(Status.CANCELED)); assertThat(actionStatusRepository.findAll()).as("wrong size of action status").hasSize(9); - assertThat(deploymentManagement.getAssignedDistributionSet("4712").get()).as("wrong assigned ds") - .isEqualTo(dsInstalled); + assertThat(deploymentManagement.getAssignedDistributionSet("4712")).as("wrong assigned ds") + .contains(dsInstalled); assertThat(targetManagement.getByControllerID("4712").get().getUpdateStatus()).as("wrong update status") .isEqualTo(TargetUpdateStatus.IN_SYNC); } @@ -333,7 +337,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Description("Cancels multiple active actions on a target. Expected behaviour is that with two active " + "actions after canceling the first active action the system switched to second one. After canceling this one " + "also the target goes back to IN_SYNC as no open action is left.") - public void manualCancelWithMultipleAssignmentsCancelMiddleOneFirst() { + void manualCancelWithMultipleAssignmentsCancelMiddleOneFirst() { final Action action = prepareFinishedUpdate("4712", "installed", true); final Target target = action.getTarget(); final DistributionSet dsFirst = testdataFactory.createDistributionSet("", true); @@ -358,8 +362,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { controllerManagement.addCancelActionStatus( entityFactory.actionStatus().create(firstAction.getId()).status(Status.CANCELED)); assertThat(actionStatusRepository.findAll()).as("wrong size of action status").hasSize(7); - assertThat(deploymentManagement.getAssignedDistributionSet("4712").get()).as("wrong assigned ds") - .isEqualTo(dsSecond); + assertThat(deploymentManagement.getAssignedDistributionSet("4712")).as("wrong assigned ds").contains(dsSecond); assertThat(targetManagement.getByControllerID("4712").get().getUpdateStatus()).as("wrong target update status") .isEqualTo(TargetUpdateStatus.PENDING); @@ -367,21 +370,20 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { deploymentManagement.cancelAction(secondAction.getId()); secondAction = (JpaAction) deploymentManagement.findActionWithDetails(secondAction.getId()).get(); assertThat(actionStatusRepository.findAll()).as("wrong size of action status").hasSize(8); - assertThat(deploymentManagement.getAssignedDistributionSet("4712").get()).as("wrong assigned ds") - .isEqualTo(dsSecond); + assertThat(deploymentManagement.getAssignedDistributionSet("4712")).as("wrong assigned ds").contains(dsSecond); // confirm cancellation controllerManagement.addCancelActionStatus( entityFactory.actionStatus().create(secondAction.getId()).status(Status.CANCELED)); // cancelled success -> back to dsInstalled - assertThat(deploymentManagement.getAssignedDistributionSet("4712").get()).as("wrong installed ds") - .isEqualTo(dsInstalled); + assertThat(deploymentManagement.getAssignedDistributionSet("4712")).as("wrong installed ds") + .contains(dsInstalled); assertThat(targetManagement.getByControllerID("4712").get().getUpdateStatus()) .as("wrong target info update status").isEqualTo(TargetUpdateStatus.IN_SYNC); } @Test @Description("Force Quit an Assignment. Expected behaviour is that the action is canceled and is marked as deleted. The assigned Software module") - public void forceQuitSetActionToInactive() throws InterruptedException { + void forceQuitSetActionToInactive() throws InterruptedException { final Action action = prepareFinishedUpdate("4712", "installed", true); final Target target = action.getTarget(); final DistributionSet dsInstalled = action.getDistributionSet(); @@ -408,15 +410,15 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { // verify assertThat(assigningAction.getStatus()).as("wrong size of status").isEqualTo(Status.CANCELED); - assertThat(deploymentManagement.getAssignedDistributionSet("4712").get()).as("wrong assigned ds") - .isEqualTo(dsInstalled); + assertThat(deploymentManagement.getAssignedDistributionSet("4712")).as("wrong assigned ds") + .contains(dsInstalled); assertThat(targetManagement.getByControllerID("4712").get().getUpdateStatus()).as("wrong target update status") .isEqualTo(TargetUpdateStatus.IN_SYNC); } @Test @Description("Force Quit an not canceled Assignment. Expected behaviour is that the action can not be force quit and there is thrown an exception.") - public void forceQuitNotAllowedThrowsException() { + void forceQuitNotAllowedThrowsException() { final Action action = prepareFinishedUpdate("4712", "installed", true); final Target target = action.getTarget(); @@ -442,8 +444,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { assignDistributionSet(ds.getId(), target.getControllerId()); assertThat(targetManagement.getByControllerID(target.getControllerId()).get().getUpdateStatus()) .as("wrong update status").isEqualTo(TargetUpdateStatus.PENDING); - assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).get()) - .as("wrong assigned ds").isEqualTo(ds); + assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId())).as("wrong assigned ds") + .contains(ds); final JpaAction action = actionRepository .findByTargetAndDistributionSet(PAGE, (JpaTarget) target, (JpaDistributionSet) ds).getContent().get(0); assertThat(action).as("action should not be null").isNotNull(); @@ -458,7 +460,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = DistributionSetCreatedEvent.class, count = 2), @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) - public void assignedDistributionSet() { + void assignedDistributionSet() { final List controllerIds = testdataFactory.createTargets(10).stream().map(Target::getControllerId) .collect(Collectors.toList()); @@ -494,16 +496,16 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { } @Test - @Description("Offline assign multiple DSs to multiple Targets in multiassignment mode.") - @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 2), + @Description("Offline assign multiple DSs to a single Target in multiassignment mode.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 4), @Expect(type = ActionCreatedEvent.class, count = 4), - @Expect(type = DistributionSetCreatedEvent.class, count = 2), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetCreatedEvent.class, count = 4), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 12), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) - public void multiOfflineAssignment() { - final List targetIds = testdataFactory.createTargets(2).stream().map(Target::getControllerId) + void multiOfflineAssignment() { + final List targetIds = testdataFactory.createTargets(1).stream().map(Target::getControllerId) .collect(Collectors.toList()); - final List dsIds = testdataFactory.createDistributionSets(2).stream().map(DistributionSet::getId) + final List dsIds = testdataFactory.createDistributionSets(4).stream().map(DistributionSet::getId) .collect(Collectors.toList()); enableMultiAssignments(); @@ -533,7 +535,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = TargetAssignDistributionSetEvent.class, count = 2), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1), @Expect(type = TenantConfigurationUpdatedEvent.class, count = 1) }) - public void assignDistributionSetAndAutoCloseActiveActions() { + void assignDistributionSetAndAutoCloseActiveActions() { tenantConfigurationManagement .addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, true); @@ -572,7 +574,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = MultiActionCancelEvent.class, count = 0), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) - public void previousAssignmentsAreNotCanceledInMultiAssignMode() { + void previousAssignmentsAreNotCanceledInMultiAssignMode() { enableMultiAssignments(); final List targets = testdataFactory.createTargets(10); final List targetIds = targets.stream().map(Target::getControllerId).collect(Collectors.toList()); @@ -606,17 +608,17 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { } @Test - @Description("Assign multiple DSs to multiple Targets in one request in multiassignment mode.") - @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 2), + @Description("Assign multiple DSs to a single Target in one request in multiassignment mode.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 4), @Expect(type = ActionCreatedEvent.class, count = 4), - @Expect(type = DistributionSetCreatedEvent.class, count = 2), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetCreatedEvent.class, count = 4), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 12), @Expect(type = MultiActionAssignEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) - public void multiAssignmentInOneRequest() { - final List targets = testdataFactory.createTargets(2); - final List distributionSets = testdataFactory.createDistributionSets(2); + void multiAssignmentInOneRequest() { + final List targets = testdataFactory.createTargets(1); + final List distributionSets = testdataFactory.createDistributionSets(4); final List deploymentRequests = createAssignmentRequests(distributionSets, targets, 34); enableMultiAssignments(); @@ -636,19 +638,19 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { } @Test - @Description("Assign multiple DSs to multiple Targets in one request in multiAssignment mode and cancel each created action afterwards.") - @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 2), + @Description("Assign multiple DSs to single Target in one request in multiAssignment mode and cancel each created action afterwards.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 4), @Expect(type = ActionCreatedEvent.class, count = 4), - @Expect(type = DistributionSetCreatedEvent.class, count = 2), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetCreatedEvent.class, count = 4), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 12), @Expect(type = MultiActionAssignEvent.class, count = 1), @Expect(type = MultiActionCancelEvent.class, count = 4), @Expect(type = ActionUpdatedEvent.class, count = 4), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) - public void cancelMultiAssignmentActions() { - final List targets = testdataFactory.createTargets(2); - final List distributionSets = testdataFactory.createDistributionSets(2); + void cancelMultiAssignmentActions() { + final List targets = testdataFactory.createTargets(1); + final List distributionSets = testdataFactory.createDistributionSets(4); final List deploymentRequests = createAssignmentRequests(distributionSets, targets, 34); enableMultiAssignments(); @@ -678,7 +680,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Test @Description("A Request resulting in multiple assignments to a single target is only allowed when multiassignment is enabled.") - public void multipleAssignmentsToTargetOnlyAllowedInMultiAssignMode() { + void multipleAssignmentsToTargetOnlyAllowedInMultiAssignMode() { final Target target = testdataFactory.createTarget(); final List distributionSets = testdataFactory.createDistributionSets(2); @@ -698,7 +700,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Assigning distribution set to the list of targets with a non-existing one leads to successful assignment of valid targets, while not found targets are silently ignored.") - public void assignDistributionSetToNotExistingTarget() { + void assignDistributionSetToNotExistingTarget() { final String notExistingId = "notExistingTarget"; final DistributionSet createdDs = testdataFactory.createDistributionSet(); @@ -714,7 +716,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { createdDs, knownTargetIds); for (final DistributionSetAssignmentResult assignDistributionSetsResult : assignDistributionSetsResults) { - assertThat(assignDistributionSetsResult.getAlreadyAssigned()).isEqualTo(0); + assertThat(assignDistributionSetsResult.getAlreadyAssigned()).isZero(); assertThat(assignDistributionSetsResult.getAssigned()).isEqualTo(2); assertThat(assignDistributionSetsResult.getTotal()).isEqualTo(2); } @@ -739,7 +741,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = ActionCreatedEvent.class, count = 3), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = MultiActionAssignEvent.class, count = 1), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) - public void duplicateAssignmentsInRequestAreOnlyRemovedIfMultiassignmentDisabled() { + void duplicateAssignmentsInRequestAreOnlyRemovedIfMultiassignmentDisabled() { final String targetId = testdataFactory.createTarget().getControllerId(); final Long dsId = testdataFactory.createDistributionSet().getId(); final List twoEqualAssignments = Collections.nCopies(2, @@ -767,7 +769,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) - public void maxActionsPerTargetIsCheckedBeforeAssignmentExecution() { + void maxActionsPerTargetIsCheckedBeforeAssignmentExecution() { final int maxActions = quotaManagement.getMaxActionsPerTarget(); final String controllerId = testdataFactory.createTarget().getControllerId(); final Long dsId = testdataFactory.createDistributionSet().getId(); @@ -778,12 +780,12 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { enableMultiAssignments(); Assertions.assertThatExceptionOfType(AssignmentQuotaExceededException.class) .isThrownBy(() -> deploymentManagement.assignDistributionSets(deploymentRequests)); - assertThat(actionRepository.countByTargetControllerId(controllerId)).isEqualTo(0); + assertThat(actionRepository.countByTargetControllerId(controllerId)).isZero(); } @Test @Description("An assignment request without a weight is ok when multi assignment in enabled.") - public void weightNotRequiredInMultiAssignmentMode() { + void weightNotRequiredInMultiAssignmentMode() { final String targetId = testdataFactory.createTarget().getControllerId(); final Long dsId = testdataFactory.createDistributionSet().getId(); @@ -800,7 +802,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) - public void weightNotAllowedWhenMultiAssignmentModeNotEnabled() { + void weightNotAllowedWhenMultiAssignmentModeNotEnabled() { final String targetId = testdataFactory.createTarget().getControllerId(); final Long dsId = testdataFactory.createDistributionSet().getId(); @@ -819,7 +821,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = MultiActionAssignEvent.class, count = 2), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) - public void weightValidatedAndSaved() { + void weightValidatedAndSaved() { final String targetId = testdataFactory.createTarget().getControllerId(); final Long dsId = testdataFactory.createDistributionSet().getId(); final DeploymentRequest valideRequest1 = DeploymentManagement.deploymentRequest(targetId, dsId) @@ -845,8 +847,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { /** * test a simple deployment by calling the - * {@link TargetRepository#assignDistributionSet(DistributionSet, Iterable)} - * and checking the active action and the action history of the targets. + * {@link TargetRepository#assignDistributionSet(DistributionSet, Iterable)} and + * checking the active action and the action history of the targets. * */ @Test @@ -856,7 +858,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = TargetCreatedEvent.class, count = 30), @Expect(type = ActionCreatedEvent.class, count = 20), @Expect(type = TargetUpdatedEvent.class, count = 20), @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) - public void assignDistributionSet2Targets() { + void assignDistributionSet2Targets() { final String myCtrlIDPref = "myCtrlID"; final Iterable savedNakedTargets = testdataFactory.createTargets(10, myCtrlIDPref, "first description"); @@ -891,7 +893,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { for (final Target myt : savedNakedTargets) { final Target t = targetManagement.getByControllerID(myt.getControllerId()).get(); assertThat(deploymentManagement.countActionsByTarget(t.getControllerId())).as("action should be empty") - .isEqualTo(0L); + .isZero(); } for (final Target myt : savedDeployedTargets) { @@ -916,7 +918,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = DistributionSetUpdatedEvent.class, count = 1) }) - public void failDistributionSetAssigmentThatIsNotComplete() throws InterruptedException { + void failDistributionSetAssigmentThatIsNotComplete() throws InterruptedException { final List targets = testdataFactory.createTargets(10); final SoftwareModule ah = testdataFactory.createSoftwareModuleApp(); @@ -947,7 +949,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = DistributionSetCreatedEvent.class, count = 3), @Expect(type = SoftwareModuleCreatedEvent.class, count = 9), @Expect(type = TargetAssignDistributionSetEvent.class, count = 3) }) - public void multipleDeployments() throws InterruptedException { + void multipleDeployments() throws InterruptedException { final String undeployedTargetPrefix = "undep-T"; final int noOfUndeployedTargets = 5; @@ -1003,7 +1005,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Description("Multiple deployments or distribution set to target assignment test including finished response " + "from target/controller. Expected behaviour is that in case of OK finished update the target will go to " + "IN_SYNC status and installed DS is set to the assigned DS entry.") - public void assignDistributionSetAndAddFinishedActionStatus() { + void assignDistributionSetAndAddFinishedActionStatus() { final PageRequest pageRequest = PageRequest.of(0, 100, Direction.ASC, ActionStatusFields.ID.getFieldName()); final DeploymentResult deployResWithDsA = prepareComplexRepo("undep-A-T", 2, "dep-A-T", 4, 1, "dsA"); @@ -1050,14 +1052,14 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { // verify, that dsA is deployed correctly for (final Target t_ : updatedTsDsA) { final Target t = targetManagement.getByControllerID(t_.getControllerId()).get(); - assertThat(deploymentManagement.getAssignedDistributionSet(t.getControllerId()).get()) - .as("assigned ds is wrong").isEqualTo(dsA); - assertThat(deploymentManagement.getInstalledDistributionSet(t.getControllerId()).get()) - .as("installed ds is wrong").isEqualTo(dsA); + assertThat(deploymentManagement.getAssignedDistributionSet(t.getControllerId())).as("assigned ds is wrong") + .contains(dsA); + assertThat(deploymentManagement.getInstalledDistributionSet(t.getControllerId())) + .as("installed ds is wrong").contains(dsA); assertThat(targetManagement.getByControllerID(t.getControllerId()).get().getUpdateStatus()) .as("wrong target info update status").isEqualTo(TargetUpdateStatus.IN_SYNC); assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, t.getControllerId())) - .as("no actions should be active").hasSize(0); + .as("no actions should be active").isEmpty(); } // deploy dsA to the target which already have dsB deployed -> must @@ -1078,8 +1080,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { for (final Target t_ : deployed2DS) { final Target t = targetManagement.getByControllerID(t_.getControllerId()).get(); - assertThat(deploymentManagement.getAssignedDistributionSet(t.getControllerId()).get()) - .as("assigned ds is wrong").isEqualTo(dsA); + assertThat(deploymentManagement.getAssignedDistributionSet(t.getControllerId())).as("assigned ds is wrong") + .contains(dsA); assertThat(deploymentManagement.getInstalledDistributionSet(t.getControllerId())) .as("installed ds should be null").isNotPresent(); assertThat(targetManagement.getByControllerID(t.getControllerId()).get().getUpdateStatus()) @@ -1089,15 +1091,14 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { } /** - * test the deletion of {@link DistributionSet}s including exception in case - * of {@link Target}s are assigned by - * {@link Target#getAssignedDistributionSet()} or - * {@link Target#getInstalledDistributionSet()} + * test the deletion of {@link DistributionSet}s including exception in case of + * {@link Target}s are assigned by {@link Target#getAssignedDistributionSet()} + * or {@link Target#getInstalledDistributionSet()} */ @Test @Description("Deletes distribution set. Expected behaviour is that a soft delete is performed " + "if the DS is assigned to a target and a hard delete if the DS is not in use at all.") - public void deleteDistributionSet() { + void deleteDistributionSet() { final PageRequest pageRequest = PageRequest.of(0, 100, Direction.ASC, "id"); @@ -1128,7 +1129,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { // verify that deleted attribute is used correctly List allFoundDS = distributionSetManagement.findByCompleted(PAGE, true).getContent(); - assertThat(allFoundDS.size()).as("no ds should be founded").isEqualTo(0); + assertThat(allFoundDS.size()).as("no ds should be founded").isZero(); assertThat(distributionSetRepository.findAll(SpecificationsBuilder.combineWithAnd(Arrays .asList(DistributionSetSpecification.isDeleted(true), DistributionSetSpecification.isCompleted(true))), @@ -1145,7 +1146,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { // successfully and no activeAction is referring to created distribution // sets allFoundDS = distributionSetManagement.findByCompleted(pageRequest, true).getContent(); - assertThat(allFoundDS.size()).as("no ds should be founded").isEqualTo(0); + assertThat(allFoundDS.size()).as("no ds should be founded").isZero(); assertThat(distributionSetRepository.findAll(SpecificationsBuilder.combineWithAnd(Arrays .asList(DistributionSetSpecification.isDeleted(true), DistributionSetSpecification.isCompleted(true))), PAGE).getContent()).as("wrong size of founded ds").hasSize(noOfDistributionSets); @@ -1153,8 +1154,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { } @Test - @Description("Deletes multiple targets and verfies that all related metadata is also deleted.") - public void deletesTargetsAndVerifyCascadeDeletes() { + @Description("Deletes multiple targets and verifies that all related metadata is also deleted.") + void deletesTargetsAndVerifyCascadeDeletes() { final String undeployedTargetPrefix = "undep-T"; final int noOfUndeployedTargets = 2; @@ -1184,7 +1185,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Testing if changing target and the status without refreshing the entities from the DB (e.g. concurrent changes from UI and from controller) works") - public void alternatingAssignmentAndAddUpdateActionStatus() { + void alternatingAssignmentAndAddUpdateActionStatus() { final DistributionSet dsA = testdataFactory.createDistributionSet("a"); final DistributionSet dsB = testdataFactory.createDistributionSet("b"); @@ -1207,8 +1208,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { assertThat(deploymentManagement.countActionsByTarget(targ.getControllerId())).as("Target actions are wrong") .isEqualTo(1); assertThat(targ.getUpdateStatus()).as("UpdateStatus of target is wrong").isEqualTo(TargetUpdateStatus.PENDING); - assertThat(deploymentManagement.getAssignedDistributionSet(targ.getControllerId()).get()) - .as("Assigned distribution set of target is wrong").isEqualTo(dsA); + assertThat(deploymentManagement.getAssignedDistributionSet(targ.getControllerId())) + .as("Assigned distribution set of target is wrong").contains(dsA); assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, targ.getControllerId()).getContent().get(0) .getDistributionSet()).as("Distribution set of actionn is wrong").isEqualTo(dsA); assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, targ.getControllerId()).getContent().get(0) @@ -1253,9 +1254,9 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { } @Test - @Description("The test verfies that the DS itself is not changed because of an target assignment" + @Description("The test verifies that the DS itself is not changed because of an target assignment" + " which is a relationship but not a changed on the entity itself..") - public void checkThatDsRevisionsIsNotChangedWithTargetAssignment() { + void checkThatDsRevisionsIsNotChangedWithTargetAssignment() { final DistributionSet dsA = testdataFactory.createDistributionSet("a"); testdataFactory.createDistributionSet("b"); final Target targ = testdataFactory.createTarget("target-id-A"); @@ -1271,7 +1272,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Tests the switch from a soft to hard update by API") - public void forceSoftAction() { + void forceSoftAction() { // prepare final Target target = testdataFactory.createTarget("knownControllerId"); final DistributionSet ds = testdataFactory.createDistributionSet("a"); @@ -1293,7 +1294,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Tests the switch from a hard to hard update by API, e.g. which in fact should not change anything.") - public void forceAlreadyForcedActionNothingChanges() { + void forceAlreadyForcedActionNothingChanges() { // prepare final Target target = testdataFactory.createTarget("knownControllerId"); final DistributionSet ds = testdataFactory.createDistributionSet("a"); @@ -1301,7 +1302,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { final DistributionSetAssignmentResult assignDistributionSet = assignDistributionSet(ds.getId(), target.getControllerId(), ActionType.FORCED); final Long actionId = getFirstAssignedActionId(assignDistributionSet); - // verify perparation + // verify preparation Action findAction = deploymentManagement.findAction(actionId).get(); assertThat(findAction.getActionType()).as("action type is wrong").isEqualTo(ActionType.FORCED); @@ -1316,7 +1317,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Tests the computation of already assigned entities returned as a result of an assignment") - public void testAlreadyAssignedAndAssignedActionsInAssignmentResult() { + void testAlreadyAssignedAndAssignedActionsInAssignmentResult() { // create target1, distributionSet, assign ds to target1 and finish // update (close all actions) final Action action = prepareFinishedUpdate("target1", "ds", false); @@ -1344,7 +1345,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that the DistributionSetAssignmentResult not contains already assigned targets.") - public void verifyDistributionSetAssignmentResultNotContainsAlreadyAssignedTargets() { + void verifyDistributionSetAssignmentResultNotContainsAlreadyAssignedTargets() { final DistributionSet dsToTargetAssigned = testdataFactory.createDistributionSet("ds-3"); // create assigned DS @@ -1354,7 +1355,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { assertThat(assignmentResult.getAssignedEntity()).hasSize(1); assignmentResult = assignDistributionSet(dsToTargetAssigned.getId(), savedTarget.getControllerId()); - assertThat(assignmentResult.getAssignedEntity()).hasSize(0); + assertThat(assignmentResult.getAssignedEntity()).isEmpty(); assertThat(distributionSetRepository.findAll()).hasSize(1); } @@ -1383,12 +1384,81 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { } + @Test + @Description("Verify that the DistributionSet assignments work for multiple targets of the same target type within the same request.") + void verifyDSAssignmentForMultipleTargetsWithSameTargetType() { + final DistributionSet ds = testdataFactory.createDistributionSet("test-ds"); + final TargetType targetType = testdataFactory.createTargetType("test-type", + Collections.singletonList(ds.getType())); + + final List deploymentRequests = new ArrayList<>(); + for (int i = 0; i < 100; i++) { + final Target target = testdataFactory.createTarget("test-target-" + i, "test-target-" + i, + targetType.getId()); + final DeploymentRequest deployment = DeploymentManagement + .deploymentRequest(target.getControllerId(), ds.getId()).build(); + deploymentRequests.add(deployment); + } + + deploymentManagement.assignDistributionSets(deploymentRequests); + + final List content = targetManagement.findAll(Pageable.unpaged()).getContent(); + + content.stream().map(JpaTarget.class::cast) + .forEach(jpaTarget -> assertThat(jpaTarget.getAssignedDistributionSet()).isEqualTo(ds)); + } + + @Test + @Description("Verify that the DistributionSet assignments work for multiple targets of different target types.") + void verifyDSAssignmentForMultipleTargetsWithDifferentTargetTypes() { + final DistributionSet ds = testdataFactory.createDistributionSet("test-ds"); + final TargetType targetType1 = testdataFactory.createTargetType("test-type1", + Collections.singletonList(ds.getType())); + final TargetType targetType2 = testdataFactory.createTargetType("test-type2", + Collections.singletonList(ds.getType())); + final Target target1 = testdataFactory.createTarget("test-target1", "test-target1", targetType1.getId()); + final Target target2 = testdataFactory.createTarget("test-target2", "test-target2", targetType2.getId()); + + final DeploymentRequest deployment1 = DeploymentManagement + .deploymentRequest(target1.getControllerId(), ds.getId()).build(); + final DeploymentRequest deployment2 = DeploymentManagement + .deploymentRequest(target2.getControllerId(), ds.getId()).build(); + final List deploymentRequests = Arrays.asList(deployment1, deployment2); + + deploymentManagement.assignDistributionSets(deploymentRequests); + + final Optional assignedDsTarget1 = targetManagement + .getByControllerID(target1.getControllerId()).map(JpaTarget.class::cast) + .map(JpaTarget::getAssignedDistributionSet); + final Optional assignedDsTarget2 = targetManagement + .getByControllerID(target2.getControllerId()).map(JpaTarget.class::cast) + .map(JpaTarget::getAssignedDistributionSet); + + assertThat(assignedDsTarget1).contains(ds); + assertThat(assignedDsTarget2).contains(ds); + } + + @Test + @Description("Verify that the DistributionSet assignment fails for target with incompatible target type.") + void verifyDSAssignmentFailsForTargetsWithIncompatibleTargetTypes() { + final DistributionSet ds = testdataFactory.createDistributionSet("test-ds"); + final TargetType targetType = testdataFactory.createTargetType("test-type", Collections.emptyList()); + final Target target = testdataFactory.createTarget("test-target", "test-target", targetType.getId()); + + final DeploymentRequest deploymentRequest = DeploymentManagement + .deploymentRequest(target.getControllerId(), ds.getId()).build(); + final List deploymentRequests = Collections.singletonList(deploymentRequest); + + assertThatExceptionOfType(DistributionSetTypeNotInTargetTypeException.class) + .isThrownBy(() -> deploymentManagement.assignDistributionSets(deploymentRequests)); + } + /** * Helper methods that creates 2 lists of targets and a list of distribution * sets. *

- * All created distribution sets are assigned to all targets of the - * target list deployedTargets. + * All created distribution sets are assigned to all targets of the target + * list deployedTargets. * * @param undeployedTargetPrefix * prefix to be used as target controller prefix @@ -1397,8 +1467,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { * @param deployedTargetPrefix * prefix to be used as target controller prefix * @param noOfDeployedTargets - * number of targets to which the created distribution sets - * assigned + * number of targets to which the created distribution sets assigned * @param noOfDistributionSets * number of distribution sets * @param distributionSetPrefix @@ -1434,7 +1503,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { } - private class DeploymentResult { + private static class DeploymentResult { final List deployedTargetIDs = new ArrayList<>(); final List undeployedTargetIDs = new ArrayList<>(); final List distributionSetIDs = new ArrayList<>(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java index 9344bf198..fbfb4fe73 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java @@ -13,6 +13,7 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -30,6 +31,7 @@ import org.assertj.core.api.Condition; import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.builder.RolloutCreate; import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate; +import org.eclipse.hawkbit.repository.builder.RolloutUpdate; import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.RolloutGroupDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; @@ -69,6 +71,7 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCond import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder; import org.eclipse.hawkbit.repository.model.RolloutGroupConditions; import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.TargetType; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus; import org.eclipse.hawkbit.repository.test.matcher.Expect; @@ -77,6 +80,7 @@ import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.T import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; @@ -87,20 +91,20 @@ import io.qameta.allure.Step; import io.qameta.allure.Story; /** - * Junit tests for RolloutManagment. + * Junit tests for RolloutManagement. */ @Feature("Component Tests - Repository") @Story("Rollout Management") -public class RolloutManagementTest extends AbstractJpaIntegrationTest { +class RolloutManagementTest extends AbstractJpaIntegrationTest { @BeforeEach - public void reset() { + void reset() { this.approvalStrategy.setApprovalNeeded(false); } @Test @Description("Verifies that a running action with distribution-set (A) is not canceled by a rollout which tries to also assign a distribution-set (A)") - public void rolloutShouldNotCancelRunningActionWithTheSameDistributionSet() { + void rolloutShouldNotCancelRunningActionWithTheSameDistributionSet() { // manually assign distribution set to target final String knownControllerId = "controller12345"; final DistributionSet knownDistributionSet = testdataFactory.createDistributionSet(); @@ -134,7 +138,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verifies that a running action is auto canceled by a rollout which assigns another distribution-set.") - public void rolloutAssignesNewDistributionSetAndAutoCloseActiveActions() { + void rolloutAssignesNewDistributionSetAndAutoCloseActiveActions() { tenantConfigurationManagement .addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, true); @@ -176,17 +180,17 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { } @Test - @Description("Verifies that management get access reacts as specfied on calls for non existing entities by means " + @Description("Verifies that management get access reacts as specified on calls for non existing entities by means " + "of Optional not present.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) }) - public void nonExistingEntityAccessReturnsNotPresent() { + void nonExistingEntityAccessReturnsNotPresent() { assertThat(rolloutManagement.get(NOT_EXIST_IDL)).isNotPresent(); assertThat(rolloutManagement.getByName(NOT_EXIST_ID)).isNotPresent(); assertThat(rolloutManagement.getWithDetailedStatus(NOT_EXIST_IDL)).isNotPresent(); } @Test - @Description("Verifies that management queries react as specfied on calls for non existing entities " + @Description("Verifies that management queries react as specified on calls for non existing entities " + " by means of throwing EntityNotFoundException.") @ExpectEvents({ @Expect(type = RolloutDeletedEvent.class, count = 0), @Expect(type = RolloutGroupCreatedEvent.class, count = 10), @@ -195,7 +199,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), @Expect(type = RolloutUpdatedEvent.class, count = 1), @Expect(type = RolloutCreatedEvent.class, count = 1), @Expect(type = TargetCreatedEvent.class, count = 10) }) - public void entityQueriesReferringToNotExistingEntitiesThrowsException() { + void entityQueriesReferringToNotExistingEntitiesThrowsException() { testdataFactory.createRollout("xxx"); verifyThrownExceptionBy(() -> rolloutManagement.delete(NOT_EXIST_IDL), "Rollout"); @@ -210,7 +214,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verifying that the rollout is created correctly, executing the filter and split up the targets in the correct group size.") - public void creatingRolloutIsCorrectPersisted() { + void creatingRolloutIsCorrectPersisted() { final int amountTargetsForRollout = 10; final int amountOtherTargets = 15; final int amountGroups = 5; @@ -228,7 +232,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verifying that when the rollout is started the actions for all targets in the rollout is created and the state of the first group is running as well as the corresponding actions") - public void startRolloutSetFirstGroupAndActionsInRunningStateAndOthersInScheduleState() { + void startRolloutSetFirstGroupAndActionsInRunningStateAndOthersInScheduleState() { final int amountTargetsForRollout = 10; final int amountOtherTargets = 15; final int amountGroups = 5; @@ -247,8 +251,9 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { final List scheduledGroups = rolloutGroupManagement .findByRollout(new OffsetBasedPageRequest(1, 100, Sort.by(Direction.ASC, "id")), createdRollout.getId()) .getContent(); - scheduledGroups.forEach(group -> assertThat(group.getStatus()).isEqualTo(RolloutGroupStatus.SCHEDULED) - .as("group which should be in scheduled state is in " + group.getStatus() + " state")); + scheduledGroups.forEach(group -> assertThat(group.getStatus()) + .as("group which should be in scheduled state is in " + group.getStatus() + " state") + .isEqualTo(RolloutGroupStatus.SCHEDULED)); // verify that the first group actions has been started and are in state // running final List runningActions = findActionsByRolloutAndStatus(createdRollout, Status.RUNNING); @@ -262,7 +267,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verifying that a finish condition of a group is hit the next group of the rollout is also started") - public void checkRunningRolloutsDoesNotStartNextGroupIfFinishConditionIsNotHit() { + void checkRunningRolloutsDoesNotStartNextGroupIfFinishConditionIsNotHit() { final int amountTargetsForRollout = 10; final int amountOtherTargets = 15; final int amountGroups = 5; @@ -286,23 +291,25 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { final List runningRolloutGroups = rolloutGroupManagement .findByRollout(new OffsetBasedPageRequest(0, 2, Sort.by(Direction.ASC, "id")), createdRollout.getId()) .getContent(); - runningRolloutGroups.forEach(group -> assertThat(group.getStatus()).isEqualTo(RolloutGroupStatus.RUNNING) + runningRolloutGroups.forEach(group -> assertThat(group.getStatus()) .as("group should be in running state because it should be started but it is in " + group.getStatus() - + " state")); + + " state") + .isEqualTo(RolloutGroupStatus.RUNNING)); // verify that the other groups are still in schedule state final List scheduledRolloutGroups = rolloutGroupManagement .findByRollout(new OffsetBasedPageRequest(2, 10, Sort.by(Direction.ASC, "id")), createdRollout.getId()) .getContent(); - scheduledRolloutGroups.forEach(group -> assertThat(group.getStatus()).isEqualTo(RolloutGroupStatus.SCHEDULED) + scheduledRolloutGroups.forEach(group -> assertThat(group.getStatus()) .as("group should be in scheduled state because it should not be started but it is in " - + group.getStatus() + " state")); + + group.getStatus() + " state") + .isEqualTo(RolloutGroupStatus.SCHEDULED)); } @Test // @Title("Deleting targets of a rollout") @Description("Verfiying that next group is started when targets of the group have been deleted.") - public void checkRunningRolloutsStartsNextGroupIfTargetsDeleted() { + void checkRunningRolloutsStartsNextGroupIfTargetsDeleted() { final int amountTargetsForRollout = 15; final int amountOtherTargets = 0; @@ -403,7 +410,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verfiying that the error handling action of a group is executed to pause the current rollout") - public void checkErrorHitOfGroupCallsErrorActionToPauseTheRollout() { + void checkErrorHitOfGroupCallsErrorActionToPauseTheRollout() { final int amountTargetsForRollout = 10; final int amountOtherTargets = 15; final int amountGroups = 5; @@ -446,7 +453,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verfiying a paused rollout in case of error action hit can be resumed again") - public void errorActionPausesRolloutAndRolloutGetsResumedStartsNextScheduledGroup() { + void errorActionPausesRolloutAndRolloutGetsResumedStartsNextScheduledGroup() { final int amountTargetsForRollout = 10; final int amountOtherTargets = 15; final int amountGroups = 5; @@ -497,7 +504,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verfiying that the rollout is starting group after group and gets finished at the end") - public void rolloutStartsGroupAfterGroupAndGetsFinished() { + void rolloutStartsGroupAfterGroupAndGetsFinished() { final int amountTargetsForRollout = 10; final int amountOtherTargets = 15; final int amountGroups = 5; @@ -535,7 +542,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that the targets have the right status during the rollout.") - public void countCorrectStatusForEachTargetDuringRollout() { + void countCorrectStatusForEachTargetDuringRollout() { final int amountTargetsForRollout = 8; final int amountOtherTargets = 15; @@ -598,7 +605,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that the targets have the right status during a download_only rollout.") - public void countCorrectStatusForEachTargetDuringDownloadOnlyRollout() { + void countCorrectStatusForEachTargetDuringDownloadOnlyRollout() { final int amountTargetsForRollout = 8; final int amountOtherTargets = 15; @@ -665,7 +672,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that the targets have the right status during the rollout when an error emerges.") - public void countCorrectStatusForEachTargetDuringRolloutWithError() { + void countCorrectStatusForEachTargetDuringRolloutWithError() { final int amountTargetsForRollout = 8; final int amountOtherTargets = 15; @@ -702,7 +709,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that the targets have the right status during the rollout when receiving the status of rollout groups.") - public void countCorrectStatusForEachTargetGroupDuringRollout() { + void countCorrectStatusForEachTargetGroupDuringRollout() { final int amountTargetsForRollout = 9; final int amountOtherTargets = 15; @@ -743,7 +750,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that target actions of rollout get canceled when a manuel distribution sets assignment is done.") - public void targetsOfRolloutGetsManuelDsAssignment() { + void targetsOfRolloutGetsManuelDsAssignment() { final int amountTargetsForRollout = 10; final int amountOtherTargets = 0; @@ -786,7 +793,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that target actions of a rollout get cancelled when another rollout with same targets gets started.") - public void targetsOfRolloutGetDistributionSetAssignmentByOtherRollout() { + void targetsOfRolloutGetDistributionSetAssignmentByOtherRollout() { final int amountTargetsForRollout = 15; final int amountOtherTargets = 5; @@ -827,7 +834,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that error status of DistributionSet installation during rollout can get rerun with second rollout so that all targets have some DistributionSet installed at the end.") - public void startSecondRolloutAfterFristRolloutEndenWithErrors() { + void startSecondRolloutAfterFristRolloutEndenWithErrors() { final int amountTargetsForRollout = 15; final int amountOtherTargets = 0; @@ -880,12 +887,12 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { // 15 targets in finished/IN_SYNC status and same DS assigned assertThat(targetList.size()).isEqualTo(amountTargetsForRollout); targetList.stream().map(Target::getControllerId).map(deploymentManagement::getAssignedDistributionSet) - .forEach(d -> assertThat(d.get()).isEqualTo(distributionSet)); + .forEach(d -> assertThat(d).contains(distributionSet)); } @Test @Description("Verify that the rollout moves to the next group when the success condition was achieved and the error condition was not exceeded.") - public void successConditionAchievedAndErrorConditionNotExceeded() { + void successConditionAchievedAndErrorConditionNotExceeded() { final int amountTargetsForRollout = 10; final int amountOtherTargets = 0; @@ -910,7 +917,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that the rollout does not move to the next group when the sucess condition was not achieved.") - public void successConditionNotAchieved() { + void successConditionNotAchieved() { final int amountTargetsForRollout = 10; final int amountOtherTargets = 0; @@ -935,7 +942,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that the rollout pauses when the error condition was exceeded.") - public void errorConditionExceeded() { + void errorConditionExceeded() { final int amountTargetsForRollout = 10; final int amountOtherTargets = 0; @@ -951,12 +958,12 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { rolloutManagement.handleRollouts(); // verify: 40% error -> should pause because errorCondition is 20% rolloutOne = rolloutManagement.get(rolloutOne.getId()).get(); - assertThat(RolloutStatus.PAUSED).isEqualTo(rolloutOne.getStatus()); + assertThat(rolloutOne.getStatus()).isEqualTo(RolloutStatus.PAUSED); } @Test @Description("Verify that all rollouts are return with expected target statuses.") - public void findAllRolloutsWithDetailedStatus() { + void findAllRolloutsWithDetailedStatus() { final int amountTargetsForRollout = 12; final int amountGroups = 2; @@ -1032,7 +1039,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify the count of existing rollouts.") - public void rightCountForAllRollouts() { + void rightCountForAllRollouts() { final int amountTargetsForRollout = 6; final int amountGroups = 2; @@ -1048,7 +1055,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify the count of filtered existing rollouts.") - public void countRolloutsAllByFilters() { + void countRolloutsAllByFilters() { final int amountTargetsForRollout = 6; final int amountGroups = 2; @@ -1070,7 +1077,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that the filtering and sorting ascending for rollout is working correctly.") - public void findRolloutByFilters() { + void findRolloutByFilters() { final int amountTargetsForRollout = 6; final int amountGroups = 2; @@ -1098,7 +1105,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that the expected rollout is found by name.") - public void findRolloutByName() { + void findRolloutByName() { final int amountTargetsForRollout = 12; final int amountGroups = 2; @@ -1115,7 +1122,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that the percent count is acting like aspected when targets move to the status finished or error.") - public void getFinishedPercentForRunningGroup() { + void getFinishedPercentForRunningGroup() { final int amountTargetsForRollout = 10; final int amountGroups = 2; @@ -1161,7 +1168,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that the expected targets are returned for the rollout groups.") - public void findRolloutGroupTargetsWithRsqlParam() { + void findRolloutGroupTargetsWithRsqlParam() { final int amountTargetsForRollout = 15; final int amountGroups = 3; @@ -1213,7 +1220,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify the creation of a Rollout without targets throws an Exception.") - public void createRolloutNotMatchingTargets() { + void createRolloutNotMatchingTargets() { final int amountGroups = 5; final String successCondition = "50"; final String errorCondition = "80"; @@ -1230,7 +1237,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify the creation of a Rollout with the same name throws an Exception.") - public void createDuplicateRollout() { + void createDuplicateRollout() { final int amountGroups = 5; final int amountTargetsForRollout = 10; final String successCondition = "50"; @@ -1251,7 +1258,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify the creation and the start of a Rollout with more groups than targets.") - public void createAndStartRolloutWithEmptyGroups() throws Exception { + void createAndStartRolloutWithEmptyGroups() throws Exception { final int amountTargetsForRollout = 3; final int amountGroups = 5; final String successCondition = "50"; @@ -1273,11 +1280,11 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { assertThat(groups.get(1).getStatus()).isEqualTo(RolloutGroupStatus.READY); assertThat(groups.get(1).getTotalTargets()).isEqualTo(1); assertThat(groups.get(2).getStatus()).isEqualTo(RolloutGroupStatus.READY); - assertThat(groups.get(2).getTotalTargets()).isEqualTo(0); + assertThat(groups.get(2).getTotalTargets()).isZero(); assertThat(groups.get(3).getStatus()).isEqualTo(RolloutGroupStatus.READY); assertThat(groups.get(3).getTotalTargets()).isEqualTo(1); assertThat(groups.get(4).getStatus()).isEqualTo(RolloutGroupStatus.READY); - assertThat(groups.get(4).getTotalTargets()).isEqualTo(0); + assertThat(groups.get(4).getTotalTargets()).isZero(); rolloutManagement.start(myRollout.getId()); @@ -1300,7 +1307,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify the creation and the start of a rollout.") - public void createAndStartRollout() throws Exception { + void createAndStartRollout() throws Exception { final int amountTargetsForRollout = 50; final int amountGroups = 5; @@ -1341,7 +1348,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify that a rollout cannot be created if the 'max targets per rollout group' quota is violated.") - public void createRolloutFailsIfQuotaGroupQuotaIsViolated() throws Exception { + void createRolloutFailsIfQuotaGroupQuotaIsViolated() throws Exception { final int maxTargets = quotaManagement.getMaxTargetsPerRolloutGroup(); @@ -1359,16 +1366,16 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { .errorCondition(RolloutGroupErrorCondition.THRESHOLD, errorCondition) .errorAction(RolloutGroupErrorAction.PAUSE, null).build(); - assertThatExceptionOfType(AssignmentQuotaExceededException.class).isThrownBy(() -> rolloutManagement.create( - entityFactory.rollout().create().name(rolloutName).description(rolloutName) - .targetFilterQuery("controllerId==" + targetPrefixName + "-*").set(distributionSet), - amountGroups, conditions)); + final RolloutCreate rollout = entityFactory.rollout().create().name(rolloutName).description(rolloutName) + .targetFilterQuery("controllerId==" + targetPrefixName + "-*").set(distributionSet); + assertThatExceptionOfType(AssignmentQuotaExceededException.class) + .isThrownBy(() -> rolloutManagement.create(rollout, amountGroups, conditions)); } @Test @Description("Verify that a rollout cannot be created based on group definitions if the 'max targets per rollout group' quota is violated for one of the groups.") - public void createRolloutWithGroupDefinitionsFailsIfQuotaGroupQuotaIsViolated() throws Exception { + void createRolloutWithGroupDefinitionsFailsIfQuotaGroupQuotaIsViolated() throws Exception { final int maxTargets = quotaManagement.getMaxTargetsPerRolloutGroup(); @@ -1422,7 +1429,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify the creation and the automatic start of a rollout.") - public void createAndAutoStartRollout() throws Exception { + void createAndAutoStartRollout() throws Exception { final int amountTargetsForRollout = 50; final int amountGroups = 5; @@ -1472,7 +1479,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify the creation of a rollout with a groups definition.") - public void createRolloutWithGroupDefinition() throws Exception { + void createRolloutWithGroupDefinition() throws Exception { final String rolloutName = "rolloutTest3"; final int amountTargetsInGroup1 = 100; @@ -1532,7 +1539,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify rollout creation fails if group definition does not address all targets") - public void createRolloutWithGroupsNotMatchingTargets() throws Exception { + void createRolloutWithGroupsNotMatchingTargets() throws Exception { final String rolloutName = "rolloutTest4"; final int amountTargetsForRollout = 500; final int percentTargetsInGroup1 = 20; @@ -1553,7 +1560,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify rollout creation fails if group definition specifies illegal target percentage") - public void createRolloutWithIllegalPercentage() throws Exception { + void createRolloutWithIllegalPercentage() throws Exception { final String rolloutName = "rolloutTest6"; final int amountTargetsForRollout = 10; final int percentTargetsInGroup1 = 101; @@ -1574,7 +1581,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify rollout creation fails if the 'max rollout groups per rollout' quota is violated.") - public void createRolloutWithIllegalAmountOfGroups() throws Exception { + void createRolloutWithIllegalAmountOfGroups() throws Exception { final String rolloutName = "rolloutTest5"; final int targets = 10; final int maxGroups = quotaManagement.getMaxRolloutGroupsPerRollout(); @@ -1590,7 +1597,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verify the start of a Rollout does not work during creation phase.") - public void createAndStartRolloutDuringCreationFails() throws Exception { + void createAndStartRolloutDuringCreationFails() throws Exception { final int amountTargetsForRollout = 3; final int amountGroups = 5; final String successCondition = "50"; @@ -1623,7 +1630,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Creating a rollout with approval role or approval engine disabled results in the rollout being in " + "READY state.") - public void createdRolloutWithApprovalRoleOrApprovalDisabledTransitionsToReadyState() { + void createdRolloutWithApprovalRoleOrApprovalDisabledTransitionsToReadyState() { approvalStrategy.setApprovalNeeded(false); final String successCondition = "50"; final String errorCondition = "80"; @@ -1635,7 +1642,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Creating a rollout without approver role and approval enabled leads to transition to " + "WAITING_FOR_APPROVAL state.") - public void createdRolloutWithoutApprovalRoleTransitionsToWaitingForApprovalState() { + void createdRolloutWithoutApprovalRoleTransitionsToWaitingForApprovalState() { approvalStrategy.setApprovalNeeded(true); final String successCondition = "50"; final String errorCondition = "80"; @@ -1646,7 +1653,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Approving a rollout leads to transition to READY state.") - public void approvedRolloutTransitionsToReadyState() { + void approvedRolloutTransitionsToReadyState() { approvalStrategy.setApprovalNeeded(true); final String successCondition = "50"; final String errorCondition = "80"; @@ -1660,7 +1667,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Denying approval for a rollout leads to transition to APPROVAL_DENIED state.") - public void deniedRolloutTransitionsToApprovalDeniedState() { + void deniedRolloutTransitionsToApprovalDeniedState() { approvalStrategy.setApprovalNeeded(true); final String successCondition = "50"; final String errorCondition = "80"; @@ -1681,7 +1688,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), @Expect(type = RolloutGroupUpdatedEvent.class, count = 5), @Expect(type = RolloutCreatedEvent.class, count = 1) }) - public void deleteRolloutWhichHasNeverStartedIsHardDeleted() { + void deleteRolloutWhichHasNeverStartedIsHardDeleted() { final int amountTargetsForRollout = 10; final int amountOtherTargets = 15; final int amountGroups = 5; @@ -1713,7 +1720,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Expect(type = ActionCreatedEvent.class, count = 10), @Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = RolloutDeletedEvent.class, count = 1), @Expect(type = RolloutCreatedEvent.class, count = 1) }) - public void deleteRolloutWhichHasBeenStartedBeforeIsSoftDeleted() { + void deleteRolloutWhichHasBeenStartedBeforeIsSoftDeleted() { final int amountTargetsForRollout = 10; final int amountOtherTargets = 15; final int amountGroups = 5; @@ -1740,19 +1747,20 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { assertThat(deletedRollout).isNotNull(); assertThat(deletedRollout.getStatus()).isEqualTo(RolloutStatus.DELETED); + + final RolloutUpdate rolloutUpdate = entityFactory.rollout().update(createdRollout.getId()).description("test"); assertThatExceptionOfType(EntityReadOnlyException.class) - .isThrownBy(() -> rolloutManagement - .update(entityFactory.rollout().update(createdRollout.getId()).description("test"))) + .isThrownBy(() -> rolloutManagement.update(rolloutUpdate)) .withMessageContaining("" + createdRollout.getId()); assertThat(rolloutManagement.findAll(PAGE, true).getContent()).hasSize(1); - assertThat(rolloutManagement.findAll(PAGE, false).getContent()).hasSize(0); + assertThat(rolloutManagement.findAll(PAGE, false).getContent()).isEmpty(); assertThat(rolloutGroupManagement.findByRolloutWithDetailedStatus(PAGE, createdRollout.getId()).getContent()) .hasSize(amountGroups); // verify that all scheduled actions are deleted assertThat(actionRepository.findByRolloutIdAndStatus(PAGE, deletedRollout.getId(), Status.SCHEDULED) - .getNumberOfElements()).isEqualTo(0); + .getNumberOfElements()).isZero(); // verify that all running actions keep running assertThat(actionRepository.findByRolloutIdAndStatus(PAGE, deletedRollout.getId(), Status.RUNNING) .getNumberOfElements()).isEqualTo(2); @@ -1760,14 +1768,14 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Creating a rollout without weight value when multi assignment in enabled.") - public void weightNotRequiredInMultiAssignmentMode() { + void weightNotRequiredInMultiAssignmentMode() { enableMultiAssignments(); createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 2, "50", "80", ActionType.FORCED, null); } @Test @Description("Creating a rollout with a weight causes an error when multi assignment in disabled.") - public void weightNotAllowedWhenMultiAssignmentModeNotEnabled() { + void weightNotAllowedWhenMultiAssignmentModeNotEnabled() { Assertions.assertThatExceptionOfType(MultiAssignmentIsNotEnabledException.class) .isThrownBy(() -> createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 2, "50", "80", ActionType.FORCED, 66)); @@ -1775,7 +1783,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Weight is validated and saved to the Rollout.") - public void weightValidatedAndSaved() { + void weightValidatedAndSaved() { final String targetPrefix = UUID.randomUUID().toString(); testdataFactory.createTargets(4, targetPrefix); enableMultiAssignments(); @@ -1798,7 +1806,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("A Rollout with weight creats actions with weights") - public void actionsWithWeightAreCreated() { + void actionsWithWeightAreCreated() { final int amountOfTargets = 5; final int weight = 99; enableMultiAssignments(); @@ -1813,7 +1821,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Rollout can be created without weight in single assignment and be started in multi assignment") - public void createInSingleStartInMultiassigMode() { + void createInSingleStartInMultiassigMode() { final int amountOfTargets = 5; final Long rolloutId = createSimpleTestRolloutWithTargetsAndDistributionSet(amountOfTargets, 2, amountOfTargets, "80", "50", null, null).getId(); @@ -1876,6 +1884,49 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest { entityFactory.rollout().update(rollout.getId()).set(incompleteDistributionSet.getId()))); } + @Test + @Description("Verify the that only compatible targets are part of a Rollout.") + void createAndStartRolloutWithTargetTypes() { + final String rolloutName = "rolloutTestCompatibility"; + + final DistributionSet testDs = testdataFactory.createDistributionSet("test-ds"); + final TargetType incompatibleTargetType = testdataFactory.createTargetType("incompatible-type", + Collections.emptyList()); + final TargetType compatibleTargetType = testdataFactory.createTargetType("compatible-type", + Collections.singletonList(testDs.getType())); + + final List incompatibleTargets = testdataFactory.createTargetsWithType(10, "incompatible", + incompatibleTargetType); + final List targetsWithoutType = testdataFactory.createTargets(10, "testTarget-"); + final List targets = testdataFactory.createTargetsWithType(10, "compatibleTarget-", + compatibleTargetType); + targets.addAll(targetsWithoutType); + + final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults().build(); + final RolloutCreate rolloutToCreate = entityFactory.rollout().create().name(rolloutName) + .targetFilterQuery("name==*").set(testDs); + + final Rollout createdRollout = rolloutManagement.create(rolloutToCreate, 1, conditions); + + // Let the executor handle created Rollout + rolloutManagement.handleRollouts(); + + final Rollout testRollout = rolloutManagement.get(createdRollout.getId()).get(); + final List rolloutGroups = rolloutGroupManagement + .findByRollout(Pageable.unpaged(), testRollout.getId()).getContent(); + + assertThat(testRollout.getStatus()).isEqualTo(RolloutStatus.READY); + assertThat(testRollout.getTotalTargets()).isEqualTo(targets.size()); + assertThat(rolloutGroups).hasSize(1); + assertThat(rolloutGroups.get(0).getTotalTargets()).isEqualTo(targets.size()); + + final List rolloutGroupTargets = rolloutGroupManagement + .findTargetsOfRolloutGroup(Pageable.unpaged(), rolloutGroups.get(0).getId()).getContent(); + + assertThat(rolloutGroupTargets).hasSize(targets.size()).containsExactlyInAnyOrderElementsOf(targets) + .doesNotContainAnyElementsOf(incompatibleTargets); + } + private RolloutGroupCreate generateRolloutGroup(final int index, final Integer percentage, final String targetFilter) { return entityFactory.rolloutGroup().create().name("Group" + index).description("Group" + index + "desc") diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java index e3f4cc87c..667ada5b9 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java @@ -22,12 +22,15 @@ import java.util.stream.Collectors; import org.eclipse.hawkbit.repository.FilterParams; import org.eclipse.hawkbit.repository.UpdateMode; +import org.eclipse.hawkbit.repository.builder.DistributionSetCreate; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.repository.model.TargetTag; +import org.eclipse.hawkbit.repository.model.TargetType; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity; import org.junit.jupiter.api.Test; @@ -43,13 +46,13 @@ import io.qameta.allure.Story; @Feature("Component Tests - Repository") @Story("Target Management Searches") -public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { +class TargetManagementSearchTest extends AbstractJpaIntegrationTest { @Test @Description("Tests different parameter combinations for target search operations. " + "That includes both the test itself, as a count operation with the same filters " + "and query definitions by RSQL (named and un-named).") - public void targetSearchWithVariousFilterCombinations() { + void targetSearchWithVariousFilterCombinations() { final TargetTag targTagX = targetTagManagement.create(entityFactory.tag().create().name("TargTag-X")); final TargetTag targTagY = targetTagManagement.create(entityFactory.tag().create().name("TargTag-Y")); final TargetTag targTagZ = targetTagManagement.create(entityFactory.tag().create().name("TargTag-Z")); @@ -181,12 +184,11 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { final List pending, final Target expected) { final String query = "updatestatus==pending and installedds.name==" + installedSet.getName(); assertThat(targetManagement - .findByFilters(PAGE, - new FilterParams(pending, null, null, installedSet.getId(), Boolean.FALSE, new String[0])) + .findByFilters(PAGE, new FilterParams(pending, null, null, installedSet.getId(), Boolean.FALSE)) .getContent()) .as("has number of elements").hasSize(1).as("that number is also returned by count query") .hasSize(Ints.saturatedCast(targetManagement.countByFilters(pending, null, null, - installedSet.getId(), Boolean.FALSE, new String[0]))) + installedSet.getId(), Boolean.FALSE))) .as("and contains the following elements").containsExactly(expected) .as("and filter query returns the same result") .containsAll(targetManagement.findByRsql(PAGE, query).getContent()); @@ -268,12 +270,11 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { + setA.getName() + ") and (name==*targ-A* or description==*targ-A*)"; assertThat(targetManagement - .findByFilters(PAGE, - new FilterParams(pending, null, "%targ-A%", setA.getId(), Boolean.FALSE, new String[0])) + .findByFilters(PAGE, new FilterParams(pending, null, "%targ-A%", setA.getId(), Boolean.FALSE)) .getContent()) .as("has number of elements").hasSize(1).as("that number is also returned by count query") .hasSize(Ints.saturatedCast(targetManagement.countByFilters(pending, null, "%targ-A%", - setA.getId(), Boolean.FALSE, new String[0]))) + setA.getId(), Boolean.FALSE))) .as("and contains the following elements").containsExactly(expected) .as("and filter query returns the same result") .containsAll(targetManagement.findByRsql(PAGE, query).getContent()); @@ -286,11 +287,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { + setA.getName() + ")"; assertThat(targetManagement - .findByFilters(PAGE, new FilterParams(pending, null, null, setA.getId(), Boolean.FALSE, new String[0])) - .getContent()) + .findByFilters(PAGE, new FilterParams(pending, null, null, setA.getId(), Boolean.FALSE)).getContent()) .as("has number of elements").hasSize(3).as("that number is also returned by count query") - .hasSize(Ints.saturatedCast(targetManagement.countByFilters(pending, null, null, setA.getId(), - Boolean.FALSE, new String[0]))) + .hasSize(Ints.saturatedCast( + targetManagement.countByFilters(pending, null, null, setA.getId(), Boolean.FALSE))) .as("and contains the following elements").containsAll(expected) .as("and filter query returns the same result") .containsAll(targetManagement.findByRsql(PAGE, query).getContent()); @@ -301,8 +301,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { final List expected) { final String query = "updatestatus==pending"; - assertThat(targetManagement - .findByFilters(PAGE, new FilterParams(pending, null, null, null, Boolean.FALSE, new String[0])) + assertThat(targetManagement.findByFilters(PAGE, new FilterParams(pending, null, null, null, Boolean.FALSE)) .getContent()) .as("has number of elements").hasSize(3).as("that number is also returned by count query") .hasSize(Ints.saturatedCast(targetManagement.countByFilters(pending, null, null, null, @@ -339,8 +338,8 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { .findByFilters(PAGE, new FilterParams(unknown, null, "%targ-A%", null, Boolean.FALSE, new String[0])) .getContent()).as("has number of elements").hasSize(99) .as("that number is also returned by count query") - .hasSize(Ints.saturatedCast(targetManagement.countByFilters(unknown, null, "%targ-A%", null, - Boolean.FALSE, new String[0]))) + .hasSize(Ints.saturatedCast( + targetManagement.countByFilters(unknown, null, "%targ-A%", null, Boolean.FALSE))) .as("and contains the following elements").containsAll(expected) .as("and filter query returns the same result") .containsAll(targetManagement.findByRsql(PAGE, query).getContent()); @@ -354,11 +353,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { + setA.getName() + ")"; assertThat(targetManagement - .findByFilters(PAGE, new FilterParams(unknown, null, null, setA.getId(), Boolean.FALSE, new String[0])) - .getContent()) + .findByFilters(PAGE, new FilterParams(unknown, null, null, setA.getId(), Boolean.FALSE)).getContent()) .as("has number of elements").hasSize(0).as("that number is also returned by count query") - .hasSize(Ints.saturatedCast(targetManagement.countByFilters(unknown, null, null, setA.getId(), - Boolean.FALSE, new String[0]))) + .hasSize(Ints.saturatedCast( + targetManagement.countByFilters(unknown, null, null, setA.getId(), Boolean.FALSE))) .as("and filter query returns the same result") .hasSize(targetManagement.findByRsql(PAGE, query).getContent().size()); } @@ -385,12 +383,11 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { final List expected) { final String query = "updatestatus==unknown"; - assertThat(targetManagement - .findByFilters(PAGE, new FilterParams(unknown, null, null, null, Boolean.FALSE, new String[0])) + assertThat(targetManagement.findByFilters(PAGE, new FilterParams(unknown, null, null, null, Boolean.FALSE)) .getContent()).as("has number of elements").hasSize(397) .as("that number is also returned by count query") - .hasSize(Ints.saturatedCast(targetManagement.countByFilters(unknown, null, null, null, - Boolean.FALSE, new String[0]))) + .hasSize(Ints.saturatedCast( + targetManagement.countByFilters(unknown, null, null, null, Boolean.FALSE))) .as("and contains the following elements").containsAll(expected) .as("and filter query returns the same result") .containsAll(targetManagement.findByRsql(PAGE, query).getContent()); @@ -404,11 +401,10 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { final String query = "lastcontrollerrequestat=le=${overdue_ts};updatestatus==UNKNOWN"; assertThat(targetManagement - .findByFilters(PAGE, new FilterParams(unknown, Boolean.TRUE, null, null, Boolean.FALSE, new String[0])) - .getContent()).as("has number of elements").hasSize(198) - .as("that number is also returned by count query") - .hasSize(Ints.saturatedCast(targetManagement.countByFilters(unknown, Boolean.TRUE, null, null, - Boolean.FALSE, new String[0]))) + .findByFilters(PAGE, new FilterParams(unknown, Boolean.TRUE, null, null, Boolean.FALSE)).getContent()) + .as("has number of elements").hasSize(198).as("that number is also returned by count query") + .hasSize(Ints.saturatedCast( + targetManagement.countByFilters(unknown, Boolean.TRUE, null, null, Boolean.FALSE))) .as("and contains the following elements").containsAll(expected) .as("and filter query returns the same result") .containsAll(targetManagement.findByRsql(PAGE, query).getContent()); @@ -420,12 +416,11 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { + " or installedds.name==" + setA.getName() + ")"; assertThat(targetManagement - .findByFilters(PAGE, - new FilterParams(null, null, "%targ-A%", setA.getId(), Boolean.FALSE, new String[0])) + .findByFilters(PAGE, new FilterParams(null, null, "%targ-A%", setA.getId(), Boolean.FALSE)) .getContent()) .as("has number of elements").hasSize(1).as("that number is also returned by count query") - .hasSize(Ints.saturatedCast(targetManagement.countByFilters(null, null, "%targ-A%", - setA.getId(), Boolean.FALSE, new String[0]))) + .hasSize(Ints.saturatedCast( + targetManagement.countByFilters(null, null, "%targ-A%", setA.getId(), Boolean.FALSE))) .as("and contains the following elements").containsExactly(expected) .as("and filter query returns the same result") .containsAll(targetManagement.findByRsql(PAGE, query).getContent()); @@ -436,12 +431,11 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { private void verifyThat3TargetsHaveDSAssigned(final DistributionSet setA, final List expected) { final String query = "assignedds.name==" + setA.getName() + " or installedds.name==" + setA.getName(); - assertThat(targetManagement - .findByFilters(PAGE, new FilterParams(null, null, null, setA.getId(), Boolean.FALSE, new String[0])) + assertThat(targetManagement.findByFilters(PAGE, new FilterParams(null, null, null, setA.getId(), Boolean.FALSE)) .getContent()) .as("has number of elements").hasSize(3).as("that number is also returned by count query") - .hasSize(Ints.saturatedCast(targetManagement.countByFilters(null, null, null, setA.getId(), - Boolean.FALSE, new String[0]))) + .hasSize(Ints.saturatedCast( + targetManagement.countByFilters(null, null, null, setA.getId(), Boolean.FALSE))) .as("and contains the following elements").containsAll(expected) .as("and filter query returns the same result") .containsAll(targetManagement.findByRsql(PAGE, query).getContent()); @@ -560,18 +554,17 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { @Step private void verifyThatRepositoryContains400Targets() { - assertThat(targetManagement.findByFilters(PAGE, new FilterParams(null, null, null, null, null, new String[0])) - .getContent()).as("Overall we expect that many targets in the repository").hasSize(400) - .as("which is also reflected by repository count") - .hasSize(Ints.saturatedCast(targetManagement.count())) - .as("which is also reflected by call without specification") - .containsAll(targetManagement.findAll(PAGE).getContent()); + assertThat(targetManagement.findByFilters(PAGE, new FilterParams(null, null, null, null, null)).getContent()) + .as("Overall we expect that many targets in the repository").hasSize(400) + .as("which is also reflected by repository count").hasSize(Ints.saturatedCast(targetManagement.count())) + .as("which is also reflected by call without specification") + .containsAll(targetManagement.findAll(PAGE).getContent()); } @Test @Description("Tests the correct order of targets based on selected distribution set. The system expects to have an order based on installed, assigned DS.") - public void targetSearchWithVariousFilterCombinationsAndOrderByDistributionSet() { + void targetSearchWithVariousFilterCombinationsAndOrderByDistributionSet() { final List notAssigned = testdataFactory.createTargets(3, "not", "first description"); List targAssigned = testdataFactory.createTargets(3, "assigned", "first description"); @@ -588,7 +581,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { .stream().map(Action::getTarget).collect(Collectors.toList()); final Slice result = targetManagement.findByFilterOrderByLinkedDistributionSet(PAGE, ds.getId(), - new FilterParams(null, null, null, null, Boolean.FALSE, new String[0])); + new FilterParams(null, null, null, null, Boolean.FALSE)); final Comparator byId = (e1, e2) -> Long.compare(e2.getId(), e1.getId()); @@ -608,7 +601,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { @Test @Description("Tests the correct order of targets with applied overdue filter based on selected distribution set. The system expects to have an order based on installed, assigned DS.") - public void targetSearchWithOverdueFilterAndOrderByDistributionSet() { + void targetSearchWithOverdueFilterAndOrderByDistributionSet() { final Long lastTargetQueryAlwaysOverdue = 0L; final Long lastTargetQueryNotOverdue = Instant.now().toEpochMilli(); @@ -641,7 +634,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { .stream().map(Action::getTarget).collect(Collectors.toList()); final Slice result = targetManagement.findByFilterOrderByLinkedDistributionSet(PAGE, ds.getId(), - new FilterParams(null, Boolean.TRUE, null, null, Boolean.FALSE, new String[0])); + new FilterParams(null, Boolean.TRUE, null, null, Boolean.FALSE)); final Comparator byId = (e1, e2) -> Long.compare(e2.getId(), e1.getId()); @@ -664,7 +657,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { @Test @Description("Verfies that targets with given assigned DS are returned from repository.") - public void findTargetByAssignedDistributionSet() { + void findTargetByAssignedDistributionSet() { final DistributionSet assignedSet = testdataFactory.createDistributionSet(""); testdataFactory.createTargets(10, "unassigned", "unassigned"); List assignedtargets = testdataFactory.createTargets(10, "assigned", "assigned"); @@ -672,8 +665,8 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { assignDistributionSet(assignedSet, assignedtargets); // get final updated version of targets - assignedtargets = targetManagement.getByControllerID( - assignedtargets.stream().map(target -> target.getControllerId()).collect(Collectors.toList())); + assignedtargets = targetManagement + .getByControllerID(assignedtargets.stream().map(Target::getControllerId).collect(Collectors.toList())); assertThat(targetManagement.findByAssignedDistributionSet(PAGE, assignedSet.getId())) .as("Contains the assigned targets").containsAll(assignedtargets) @@ -683,7 +676,7 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { @Test @Description("Verifies that targets without given assigned DS are returned from repository.") - public void findTargetWithoutAssignedDistributionSet() { + void findTargetWithoutAssignedDistributionSet() { final DistributionSet assignedSet = testdataFactory.createDistributionSet(""); final TargetFilterQuery tfq = targetFilterQueryManagement .create(entityFactory.targetFilterQuery().create().name("tfq").query("name==*")); @@ -693,15 +686,15 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { assignDistributionSet(assignedSet, assignedTargets); final List result = targetManagement - .findByTargetFilterQueryAndNonDS(PAGE, assignedSet.getId(), tfq.getQuery()).getContent(); + .findByTargetFilterQueryAndNonDSAndCompatible(PAGE, assignedSet.getId(), tfq.getQuery()).getContent(); assertThat(result).as("count of targets").hasSize(unassignedTargets.size()).as("contains all targets") .containsAll(unassignedTargets); } @Test - @Description("Verfies that targets with given installed DS are returned from repository.") - public void findTargetByInstalledDistributionSet() { + @Description("Verifies that targets with given installed DS are returned from repository.") + void findTargetByInstalledDistributionSet() { final DistributionSet assignedSet = testdataFactory.createDistributionSet(""); final DistributionSet installedSet = testdataFactory.createDistributionSet("another"); testdataFactory.createTargets(10, "unassigned", "unassigned"); @@ -721,4 +714,60 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest { .as("and that means the following expected amount").hasSize(10); } + + @Test + @Description("Verifies that all compatible targets are returned from repository.") + void shouldFindAllTargetsCompatibleWithDS() { + final DistributionSet testDs = testdataFactory.createDistributionSet(); + final TargetType targetType = testdataFactory.createTargetType("testType", + Collections.singletonList(testDs.getType())); + final TargetFilterQuery tfq = targetFilterQueryManagement + .create(entityFactory.targetFilterQuery().create().name("test-filter").query("name==*")); + final List targets = testdataFactory.createTargets(20, "withOutType"); + final List targetWithCompatibleTypes = testdataFactory.createTargetsWithType(20, "compatible", + targetType); + + final List result = targetManagement + .findByTargetFilterQueryAndNonDSAndCompatible(PAGE, testDs.getId(), tfq.getQuery()).getContent(); + + assertThat(result).as("count of targets").hasSize(targets.size() + targetWithCompatibleTypes.size()) + .as("contains all targets").containsAll(targetWithCompatibleTypes).containsAll(targets); + } + + @Test + @Description("Verifies that incompatible targets are not returned from repository.") + void shouldNotFindTargetsIncompatibleWithDS() { + final DistributionSetType dsType = testdataFactory.findOrCreateDistributionSetType("test-ds-type", + "test-ds-type"); + final DistributionSet testDs = createDistSetWithType(dsType); + final TargetType compatibleTargetType = testdataFactory.createTargetType("compTestType", + Collections.singletonList(dsType)); + final TargetType incompatibleTargetType = testdataFactory.createTargetType("incompTestType", + Collections.singletonList(testdataFactory.createDistributionSet().getType())); + final TargetFilterQuery tfq = targetFilterQueryManagement + .create(entityFactory.targetFilterQuery().create().name("test-filter").query("name==*")); + + final List targetsWithOutType = testdataFactory.createTargets(20, "withOutType"); + final List targetsWithCompatibleType = testdataFactory.createTargetsWithType(20, "compatible", + compatibleTargetType); + final List targetsWithIncompatibleType = testdataFactory.createTargetsWithType(20, "incompatible", + incompatibleTargetType); + + final List testTargets = new ArrayList<>(); + testTargets.addAll(targetsWithOutType); + testTargets.addAll(targetsWithCompatibleType); + + final List result = targetManagement + .findByTargetFilterQueryAndNonDSAndCompatible(PAGE, testDs.getId(), tfq.getQuery()).getContent(); + + assertThat(result).as("count of targets").hasSize(testTargets.size()).as("contains all compatible targets") + .containsExactlyInAnyOrderElementsOf(testTargets).as("does not contain incompatible targets") + .doesNotContainAnyElementsOf(targetsWithIncompatibleType); + } + + private DistributionSet createDistSetWithType(final DistributionSetType type) { + final DistributionSetCreate dsCreate = entityFactory.distributionSet().create().name("test-ds").version("1.0") + .type(type); + return distributionSetManagement.create(dsCreate); + } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java index de19b5575..bd0b06688 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java @@ -114,13 +114,14 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { "DistributionSet"); verifyThrownExceptionBy(() -> targetManagement.countByTargetFilterQuery(NOT_EXIST_IDL), "TargetFilterQuery"); - verifyThrownExceptionBy(() -> targetManagement.countByRsqlAndNonDS(NOT_EXIST_IDL, "name==*"), + verifyThrownExceptionBy(() -> targetManagement.countByRsqlAndNonDSAndCompatible(NOT_EXIST_IDL, "name==*"), "DistributionSet"); verifyThrownExceptionBy(() -> targetManagement.deleteByControllerID(NOT_EXIST_ID), "Target"); verifyThrownExceptionBy(() -> targetManagement.delete(Collections.singletonList(NOT_EXIST_IDL)), "Target"); - verifyThrownExceptionBy(() -> targetManagement.findByTargetFilterQueryAndNonDS(PAGE, NOT_EXIST_IDL, "name==*"), + verifyThrownExceptionBy( + () -> targetManagement.findByTargetFilterQueryAndNonDSAndCompatible(PAGE, NOT_EXIST_IDL, "name==*"), "DistributionSet"); verifyThrownExceptionBy(() -> targetManagement.findByInRolloutGroupWithoutAction(PAGE, NOT_EXIST_IDL), "RolloutGroup"); @@ -475,8 +476,8 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { .isEqualTo(0); assertThat(targetManagement.countByInstalledDistributionSet(set2.getId())).as("Target count is wrong") .isEqualTo(0); - assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(set2.getId())).as("Target count is wrong") - .isFalse(); + assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(set2.getId())) + .as("Target count is wrong").isFalse(); Target target = createTargetWithAttributes("4711"); @@ -502,8 +503,8 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { .isEqualTo(1); assertThat(targetManagement.countByInstalledDistributionSet(set2.getId())).as("Target count is wrong") .isEqualTo(0); - assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(set2.getId())).as("Target count is wrong") - .isTrue(); + assertThat(targetManagement.existsByInstalledOrAssignedDistributionSet(set2.getId())) + .as("Target count is wrong").isTrue(); assertThat(target.getLastTargetQuery()).as("Target query is not work").isGreaterThanOrEqualTo(current); assertThat(deploymentManagement.getAssignedDistributionSet("4711").get()).as("Assigned ds size is wrong") .isEqualTo(set2); @@ -537,14 +538,14 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { } /** - * verifies, that all {@link TargetTag} of parameter. NOTE: it's accepted - * that the target have additional tags assigned to them which are not - * contained within parameter tags. + * verifies, that all {@link TargetTag} of parameter. NOTE: it's accepted that + * the target have additional tags assigned to them which are not contained + * within parameter tags. * * @param strict - * if true, the given targets MUST contain EXACTLY ALL given - * tags, AND NO OTHERS. If false, the given targets MUST contain - * ALL given tags, BUT MAY CONTAIN FURTHER ONE + * if true, the given targets MUST contain EXACTLY ALL given tags, + * AND NO OTHERS. If false, the given targets MUST contain ALL given + * tags, BUT MAY CONTAIN FURTHER ONE * @param targets * targets to be verified * @param tags @@ -597,13 +598,16 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { Long modifiedAt = savedTarget.getLastModifiedAt(); assertThat(createdAt).as("CreatedAt compared with modifiedAt").isEqualTo(modifiedAt); - assertThat(savedTarget.getCreatedAt()).isNotNull().as("The createdAt attribute of the target should no be null"); - assertThat(savedTarget.getLastModifiedAt()).isNotNull().as("The lastModifiedAt attribute of the target should no be null"); + assertThat(savedTarget.getCreatedAt()).isNotNull() + .as("The createdAt attribute of the target should no be null"); + assertThat(savedTarget.getLastModifiedAt()).isNotNull() + .as("The lastModifiedAt attribute of the target should no be null"); Thread.sleep(1); savedTarget = targetManagement.update( entityFactory.target().update(savedTarget.getControllerId()).description("changed description")); - assertThat(savedTarget.getLastModifiedAt()).isNotNull().as("The lastModifiedAt attribute of the target should not be null"); + assertThat(savedTarget.getLastModifiedAt()).isNotNull() + .as("The lastModifiedAt attribute of the target should not be null"); assertThat(createdAt).as("CreatedAt compared with saved modifiedAt") .isNotEqualTo(savedTarget.getLastModifiedAt()); assertThat(modifiedAt).as("ModifiedAt compared with saved modifiedAt") @@ -1046,22 +1050,23 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { @Description("Checks that target type for a target can be created, updated and unassigned.") public void createAndUpdateTargetTypeInTarget() { // create a target type - List targetTypes = testdataFactory.createTargetTypes("targettype", 2); + final List targetTypes = testdataFactory.createTargetTypes("targettype", 2); assertThat(targetTypes).hasSize(2); // create a target final Target target = testdataFactory.createTarget("target1", "testtarget", targetTypes.get(0).getId()); // initial opt lock revision must be one - Optional targetFound = targetRepository.findById(target.getId()); + final Optional targetFound = targetRepository.findById(target.getId()); assertThat(targetFound).isPresent(); assertThat(targetFound.get().getOptLockRevision()).isEqualTo(1); assertThat(targetFound.get().getTargetType().getId()).isEqualTo(targetTypes.get(0).getId()); // update the target type - TargetUpdate targetUpdate = entityFactory.target().update(target.getControllerId()).targetType(targetTypes.get(1).getId()); + final TargetUpdate targetUpdate = entityFactory.target().update(target.getControllerId()) + .targetType(targetTypes.get(1).getId()); targetManagement.update(targetUpdate); // opt lock revision must be changed - Optional targetFound1 = targetRepository.findById(target.getId()); + final Optional targetFound1 = targetRepository.findById(target.getId()); assertThat(targetFound1).isPresent(); assertThat(targetFound1.get().getOptLockRevision()).isEqualTo(2); assertThat(targetFound1.get().getTargetType().getId()).isEqualTo(targetTypes.get(1).getId()); @@ -1070,7 +1075,7 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { targetManagement.unAssignType(target.getControllerId()); // opt lock revision must be changed - Optional targetFound2 = targetRepository.findById(target.getId()); + final Optional targetFound2 = targetRepository.findById(target.getId()); assertThat(targetFound2).isPresent(); assertThat(targetFound2.get().getOptLockRevision()).isEqualTo(3); assertThat(targetFound2.get().getTargetType()).isNull(); @@ -1083,20 +1088,20 @@ public class TargetManagementTest extends AbstractJpaIntegrationTest { // create a target final Target target = testdataFactory.createTarget("target1", "testtarget"); // initial opt lock revision must be one - Optional targetFound = targetRepository.findById(target.getId()); + final Optional targetFound = targetRepository.findById(target.getId()); assertThat(targetFound).isPresent(); assertThat(targetFound.get().getOptLockRevision()).isEqualTo(1); assertThat(targetFound.get().getTargetType()).isNull(); // create a target type - TargetType targetType = testdataFactory.findOrCreateTargetType("targettype"); + final TargetType targetType = testdataFactory.findOrCreateTargetType("targettype"); assertThat(targetType).isNotNull(); // assign target type to target targetManagement.assignType(targetFound.get().getControllerId(), targetType.getId()); // opt lock revision must be changed - Optional targetFound1 = targetRepository.findById(target.getId()); + final Optional targetFound1 = targetRepository.findById(target.getId()); assertThat(targetFound1).isPresent(); assertThat(targetFound1.get().getOptLockRevision()).isEqualTo(2); assertThat(targetFound1.get().getTargetType().getId()).isEqualTo(targetType.getId()); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java index 87b97040c..1afee2112 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java @@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.autoassign; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -25,6 +26,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetFilterQuery; +import org.eclipse.hawkbit.repository.model.TargetType; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -42,7 +44,7 @@ import io.qameta.allure.Story; */ @Feature("Component Tests - Repository") @Story("Auto assign checker") -public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest { +class AutoAssignCheckerTest extends AbstractJpaIntegrationTest { @Autowired private AutoAssignChecker autoAssignChecker; @@ -52,7 +54,7 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest { @Test @Description("Verifies that a running action is auto canceled by a AutoAssignment which assigns another distribution-set.") - public void autoAssignDistributionSetAndAutoCloseOldActions() { + void autoAssignDistributionSetAndAutoCloseOldActions() { tenantConfigurationManagement .addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, true); @@ -96,7 +98,7 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest { @Test @Description("Test auto assignment of a DS to filtered targets") - public void checkAutoAssign() { + void checkAutoAssign() { final DistributionSet setA = testdataFactory.createDistributionSet("dsA"); // will // be @@ -131,7 +133,8 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest { verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(10, 20), targetsCount); // Count the number of targets that will be assigned with setA - assertThat(targetManagement.countByRsqlAndNonDS(setA.getId(), targetFilterQuery.getQuery())).isEqualTo(90); + assertThat(targetManagement.countByRsqlAndNonDSAndCompatible(setA.getId(), targetFilterQuery.getQuery())) + .isEqualTo(90); // Run the check autoAssignChecker.check(); @@ -146,7 +149,7 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest { @Test @Description("Test auto assignment of an incomplete DS to filtered targets, that causes failures") - public void checkAutoAssignWithFailures() { + void checkAutoAssignWithFailures() { // incomplete distribution set that will be assigned final DistributionSet setF = distributionSetManagement.create(entityFactory.distributionSet().create() @@ -230,7 +233,7 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest { @Test @Description("Test auto assignment of a distribution set with FORCED, SOFT and DOWNLOAD_ONLY action types") - public void checkAutoAssignWithDifferentActionTypes() { + void checkAutoAssignWithDifferentActionTypes() { final DistributionSet distributionSet = testdataFactory.createDistributionSet(); final String targetDsAIdPref = "A"; final String targetDsBIdPref = "B"; @@ -280,8 +283,8 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest { } @Test - @Description("An auto assignment target filter with weight creats actions with weights") - public void actionsWithWeightAreCreated() throws Exception { + @Description("An auto assignment target filter with weight creates actions with weights") + void actionsWithWeightAreCreated() throws Exception { final int amountOfTargets = 5; final DistributionSet ds = testdataFactory.createDistributionSet(); final int weight = 32; @@ -299,7 +302,7 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest { @Test @Description("An auto assignment target filter without weight still works after multi assignment is enabled") - public void filterWithoutWeightWorksInMultiAssignmentMode() throws Exception { + void filterWithoutWeightWorksInMultiAssignmentMode() throws Exception { final int amountOfTargets = 5; final DistributionSet ds = testdataFactory.createDistributionSet(); targetFilterQueryManagement.create( @@ -313,4 +316,31 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest { assertThat(actions).hasSize(amountOfTargets); assertThat(actions).allMatch(action -> !action.getWeight().isPresent()); } + + @Test + @Description("Verifies an auto assignment only creates actions for compatible targets") + void checkAutoAssignmentWithIncompatibleTargets() throws Exception { + final DistributionSet testDs = testdataFactory.createDistributionSet(); + final TargetFilterQuery testFilter = targetFilterQueryManagement.create(entityFactory.targetFilterQuery() + .create().name("test-filter").query("name==*").autoAssignDistributionSet(testDs)); + + final TargetType incompatibleType = testdataFactory.createTargetType("incompatibleType", + Collections.emptyList()); + final TargetType compatibleType = testdataFactory.createTargetType("compatibleType", + Collections.singletonList(testDs.getType())); + testdataFactory.createTargetsWithType(10, "incompatible", incompatibleType); + final Target compatibleTarget = testdataFactory.createTargetsWithType(1, "compatible", compatibleType).get(0); + final Target targetWithoutType = testdataFactory.createTarget(); + + final long compatibleCount = targetManagement.countByRsqlAndNonDSAndCompatible(testDs.getId(), + testFilter.getQuery()); + assertThat(compatibleCount).isEqualTo(2); + + autoAssignChecker.check(); + + final List actions = deploymentManagement.findActionsAll(PAGE).getContent(); + assertThat(actions).hasSize(2); + final List actionTargets = actions.stream().map(a -> a.getTarget().getId()).collect(Collectors.toList()); + assertThat(actionTargets).containsExactlyInAnyOrder(compatibleTarget.getId(), targetWithoutType.getId()); + } } diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java index f093edbbb..40a4d9ef4 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java @@ -124,8 +124,8 @@ public class TestdataFactory { public static final String SM_TYPE_RT = "runtime"; /** - * Key of test "application" {@link SoftwareModuleType} : optional software - * in {@link #DS_TYPE_DEFAULT}. + * Key of test "application" {@link SoftwareModuleType} : optional software in + * {@link #DS_TYPE_DEFAULT}. */ public static final String SM_TYPE_APP = "application"; @@ -175,8 +175,8 @@ public class TestdataFactory { /** * Creates {@link DistributionSet} in repository including three - * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} - * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and + * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} , + * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and * {@link DistributionSet#isRequiredMigrationStep()} false. * * @param prefix @@ -191,8 +191,8 @@ public class TestdataFactory { /** * Creates {@link DistributionSet} in repository including three - * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} - * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and + * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} , + * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and * {@link DistributionSet#isRequiredMigrationStep()} false. * * @return {@link DistributionSet} entity. @@ -203,8 +203,8 @@ public class TestdataFactory { /** * Creates {@link DistributionSet} in repository including three - * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} - * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and + * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} , + * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and * {@link DistributionSet#isRequiredMigrationStep()} false. * * @param modules @@ -218,8 +218,8 @@ public class TestdataFactory { /** * Creates {@link DistributionSet} in repository including three - * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} - * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and + * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} , + * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and * {@link DistributionSet#isRequiredMigrationStep()} false. * * @param modules @@ -236,8 +236,8 @@ public class TestdataFactory { /** * Creates {@link DistributionSet} in repository including three - * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} - * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION}. + * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} , + * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION}. * * @param prefix * for {@link SoftwareModule}s and {@link DistributionSet}s name, @@ -253,8 +253,8 @@ public class TestdataFactory { /** * Creates {@link DistributionSet} in repository including three - * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} - * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and + * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} , + * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} and * {@link DistributionSet#isRequiredMigrationStep()} false. * * @param prefix @@ -271,16 +271,15 @@ public class TestdataFactory { /** * Creates {@link DistributionSet} in repository including three - * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} - * , {@link #SM_TYPE_APP}. + * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} , + * {@link #SM_TYPE_APP}. * * @param prefix * for {@link SoftwareModule}s and {@link DistributionSet}s name, * vendor and description. * @param version * {@link DistributionSet#getVersion()} and - * {@link SoftwareModule#getVersion()} extended by a random - * number. + * {@link SoftwareModule#getVersion()} extended by a random number. * @param isRequiredMigrationStep * for {@link DistributionSet#isRequiredMigrationStep()} * @@ -341,8 +340,7 @@ public class TestdataFactory { * vendor and description. * @param version * {@link DistributionSet#getVersion()} and - * {@link SoftwareModule#getVersion()} extended by a random - * number. + * {@link SoftwareModule#getVersion()} extended by a random number. * @param isRequiredMigrationStep * for {@link DistributionSet#isRequiredMigrationStep()} * @param modules @@ -362,8 +360,8 @@ public class TestdataFactory { /** * Creates {@link DistributionSet} in repository including three - * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} - * , {@link #SM_TYPE_APP}. + * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} , + * {@link #SM_TYPE_APP}. * * @param prefix * for {@link SoftwareModule}s and {@link DistributionSet}s name, @@ -390,9 +388,9 @@ public class TestdataFactory { /** * Creates {@link DistributionSet}s in repository including three - * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} - * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an - * iterative number and {@link DistributionSet#isRequiredMigrationStep()} + * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} , + * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an iterative + * number and {@link DistributionSet#isRequiredMigrationStep()} * false. * * @param number @@ -406,8 +404,7 @@ public class TestdataFactory { } /** - * Create a list of {@link DistributionSet}s without modules, i.e. - * incomplete. + * Create a list of {@link DistributionSet}s without modules, i.e. incomplete. * * @param number * of {@link DistributionSet}s to create @@ -427,9 +424,9 @@ public class TestdataFactory { /** * Creates {@link DistributionSet}s in repository including three - * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} - * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an - * iterative number and {@link DistributionSet#isRequiredMigrationStep()} + * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} , + * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an iterative + * number and {@link DistributionSet#isRequiredMigrationStep()} * false. * * @param prefix @@ -469,8 +466,8 @@ public class TestdataFactory { } /** - * Creates {@link Artifact}s for given {@link SoftwareModule} with a small - * text payload. + * Creates {@link Artifact}s for given {@link SoftwareModule} with a small text + * payload. * * @param moduleId * the {@link Artifact}s belong to. @@ -488,8 +485,8 @@ public class TestdataFactory { } /** - * Create an {@link Artifact} for given {@link SoftwareModule} with a small - * text payload. + * Create an {@link Artifact} for given {@link SoftwareModule} with a small text + * payload. * * @param artifactData * the {@link Artifact} Inputstream @@ -509,8 +506,8 @@ public class TestdataFactory { } /** - * Create an {@link Artifact} for given {@link SoftwareModule} with a small - * text payload. + * Create an {@link Artifact} for given {@link SoftwareModule} with a small text + * payload. * * @param artifactData * the {@link Artifact} Inputstream @@ -534,8 +531,8 @@ public class TestdataFactory { /** * Creates {@link SoftwareModule} with {@link #DEFAULT_VENDOR} and - * {@link #DEFAULT_VERSION} and random generated - * {@link Target#getDescription()} in the repository. + * {@link #DEFAULT_VERSION} and random generated {@link Target#getDescription()} + * in the repository. * * @param typeKey * of the {@link SoftwareModuleType} @@ -547,10 +544,9 @@ public class TestdataFactory { } /** - * Creates {@link SoftwareModule} of type - * {@value Constants#SMT_DEFAULT_APP_KEY} with {@link #DEFAULT_VENDOR} and - * {@link #DEFAULT_VERSION} and random generated - * {@link Target#getDescription()} in the repository. + * Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_APP_KEY} + * with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random + * generated {@link Target#getDescription()} in the repository. * * * @return persisted {@link SoftwareModule}. @@ -560,10 +556,9 @@ public class TestdataFactory { } /** - * Creates {@link SoftwareModule} of type - * {@value Constants#SMT_DEFAULT_APP_KEY} with {@link #DEFAULT_VENDOR} and - * {@link #DEFAULT_VERSION} and random generated - * {@link Target#getDescription()} in the repository. + * Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_APP_KEY} + * with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random + * generated {@link Target#getDescription()} in the repository. * * @param prefix * added to name and version @@ -576,10 +571,9 @@ public class TestdataFactory { } /** - * Creates {@link SoftwareModule} of type - * {@value Constants#SMT_DEFAULT_OS_KEY} with {@link #DEFAULT_VENDOR} and - * {@link #DEFAULT_VERSION} and random generated - * {@link Target#getDescription()} in the repository. + * Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_OS_KEY} + * with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random + * generated {@link Target#getDescription()} in the repository. * * * @return persisted {@link SoftwareModule}. @@ -589,10 +583,9 @@ public class TestdataFactory { } /** - * Creates {@link SoftwareModule} of type - * {@value Constants#SMT_DEFAULT_OS_KEY} with {@link #DEFAULT_VENDOR} and - * {@link #DEFAULT_VERSION} and random generated - * {@link Target#getDescription()} in the repository. + * Creates {@link SoftwareModule} of type {@value Constants#SMT_DEFAULT_OS_KEY} + * with {@link #DEFAULT_VENDOR} and {@link #DEFAULT_VERSION} and random + * generated {@link Target#getDescription()} in the repository. * * @param prefix * added to name and version @@ -606,8 +599,8 @@ public class TestdataFactory { /** * Creates {@link SoftwareModule} with {@link #DEFAULT_VENDOR} and - * {@link #DEFAULT_VERSION} and random generated - * {@link Target#getDescription()} in the repository. + * {@link #DEFAULT_VERSION} and random generated {@link Target#getDescription()} + * in the repository. * * @param typeKey * of the {@link SoftwareModuleType} @@ -679,15 +672,14 @@ public class TestdataFactory { /** * Creates {@link DistributionSet}s in repository including three - * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} - * , {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an - * iterative number and {@link DistributionSet#isRequiredMigrationStep()} + * {@link SoftwareModule}s of types {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} , + * {@link #SM_TYPE_APP} with {@link #DEFAULT_VERSION} followed by an iterative + * number and {@link DistributionSet#isRequiredMigrationStep()} * false. * * In addition it updates the created {@link DistributionSet}s and - * {@link SoftwareModule}s to ensure that - * {@link BaseEntity#getLastModifiedAt()} and - * {@link BaseEntity#getLastModifiedBy()} is filled. + * {@link SoftwareModule}s to ensure that {@link BaseEntity#getLastModifiedAt()} + * and {@link BaseEntity#getLastModifiedBy()} is filled. * * @return persisted {@link DistributionSet}. */ @@ -705,8 +697,8 @@ public class TestdataFactory { /** * @return {@link DistributionSetType} with key {@link #DS_TYPE_DEFAULT} and - * {@link SoftwareModuleType}s {@link #SM_TYPE_OS}, - * {@link #SM_TYPE_RT} , {@link #SM_TYPE_APP}. + * {@link SoftwareModuleType}s {@link #SM_TYPE_OS}, {@link #SM_TYPE_RT} + * , {@link #SM_TYPE_APP}. */ public DistributionSetType findOrCreateDefaultTestDsType() { final List mand = new ArrayList<>(); @@ -762,8 +754,8 @@ public class TestdataFactory { /** * Finds {@link SoftwareModuleType} in repository with given - * {@link SoftwareModuleType#getKey()} or creates if it does not exist yet - * with {@link SoftwareModuleType#getMaxAssignments()} = 1. + * {@link SoftwareModuleType#getKey()} or creates if it does not exist yet with + * {@link SoftwareModuleType#getMaxAssignments()} = 1. * * @param key * {@link SoftwareModuleType#getKey()} @@ -869,9 +861,8 @@ public class TestdataFactory { } /** - * Creates {@link Target}s in repository and with - * {@link #DEFAULT_CONTROLLER_ID} as prefix for - * {@link Target#getControllerId()}. + * Creates {@link Target}s in repository and with {@link #DEFAULT_CONTROLLER_ID} + * as prefix for {@link Target#getControllerId()}. * * @param number * of {@link Target}s to create @@ -888,6 +879,30 @@ public class TestdataFactory { return targetManagement.create(targets); } + /** + * Creates {@link Target}s in repository and with {@link TargetType}. + * + * @param number + * of {@link Target}s to create + * @param controllerIdPrefix + * prefix for the controller id + * @param targetType + * targetType of targets to create + * + * @return {@link List} of {@link Target} entities + */ + public List createTargetsWithType(final int number, final String controllerIdPrefix, + final TargetType targetType) { + + final List targets = Lists.newArrayListWithExpectedSize(number); + for (int i = 0; i < number; i++) { + targets.add(entityFactory.target().create().controllerId(controllerIdPrefix + i) + .targetType(targetType.getId())); + } + + return targetManagement.create(targets); + } + /** * Creates {@link Target}s in repository and with given targetIds. * @@ -929,8 +944,7 @@ public class TestdataFactory { /** * Builds {@link Target} objects with given prefix for - * {@link Target#getControllerId()} followed by a number suffix starting - * with 0. + * {@link Target#getControllerId()} followed by a number suffix starting with 0. * * @param numberOfTargets * of {@link Target}s to generate @@ -1046,8 +1060,7 @@ public class TestdataFactory { } /** - * Append {@link ActionStatus} to all {@link Action}s of given - * {@link Target}s. + * Append {@link ActionStatus} to all {@link Action}s of given {@link Target}s. * * @param targets * to add {@link ActionStatus} @@ -1064,8 +1077,7 @@ public class TestdataFactory { } /** - * Append {@link ActionStatus} to all {@link Action}s of given - * {@link Target}s. + * Append {@link ActionStatus} to all {@link Action}s of given {@link Target}s. * * @param targets * to add {@link ActionStatus} @@ -1163,8 +1175,8 @@ public class TestdataFactory { * {@link Target}s. * * @param prefix - * for rollouts name, description, - * {@link Target#getControllerId()} filter + * for rollouts name, description, {@link Target#getControllerId()} + * filter * @return created {@link Rollout} */ public Rollout createRollout(final String prefix) { @@ -1174,12 +1186,12 @@ public class TestdataFactory { } /** - * Create the soft deleted {@link Rollout} with a new - * {@link DistributionSet} and {@link Target}s. + * Create the soft deleted {@link Rollout} with a new {@link DistributionSet} + * and {@link Target}s. * * @param prefix - * for rollouts name, description, - * {@link Target#getControllerId()} filter + * for rollouts name, description, {@link Target#getControllerId()} + * filter * @return created {@link Rollout} */ public Rollout createSoftDeletedRollout(final String prefix) { @@ -1194,7 +1206,8 @@ public class TestdataFactory { /** * Finds {@link TargetType} in repository with given * {@link TargetType#getName()} or creates if it does not exist yet. No ds - * types are assigned on creation. + * types + * are assigned on creation. * * @param targetTypeName * {@link TargetType#getName()} @@ -1210,7 +1223,8 @@ public class TestdataFactory { /** * Creates {@link TargetType} in repository with given * {@link TargetType#getName()}. Compatible distribution set types are - * assigned on creation + * assigned + * on creation * * @param targetTypeName * {@link TargetType#getName()} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoAssignmentWindowController.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoAssignmentWindowController.java index 77133f2ef..834c35cfe 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoAssignmentWindowController.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/filtermanagement/AutoAssignmentWindowController.java @@ -92,7 +92,7 @@ public class AutoAssignmentWindowController extends // store/show dialog if (entity.isAutoAssignmentEnabled() && entity.getDistributionSetInfo() != null) { final Long autoAssignDsId = entity.getDistributionSetInfo().getId(); - final Long targetsForAutoAssignmentCount = targetManagement.countByRsqlAndNonDS(autoAssignDsId, + final Long targetsForAutoAssignmentCount = targetManagement.countByRsqlAndNonDSAndCompatible(autoAssignDsId, entity.getQuery()); final String confirmationCaption = getI18n() diff --git a/pom.xml b/pom.xml index f42694413..69163bb0a 100644 --- a/pom.xml +++ b/pom.xml @@ -165,7 +165,7 @@ 9.1.3 1.14.2 2.13.6 - 2.7.3 + 2.7.9 1.1.8 30.1.1-jre 2.2.4