Adapt UI for target type compatibility check (#1189)
* Added compatibility calls needed for UI Signed-off-by: Robert Sing <robert.sing@bosch-si.com> * Adapted UI for target type compatibility checks Signed-off-by: Robert Sing <robert.sing@bosch-si.com> * improved exception handling for incompatibility check Signed-off-by: Robert Sing <robert.sing@bosch-si.com> * added & fixed unit tests Signed-off-by: Robert Sing <robert.sing@bosch-si.com> * fixed merged conflicts Signed-off-by: Robert Sing <robert.sing@bosch-si.com> * fixed target type incompatibly specification Signed-off-by: Robert Sing <robert.sing@bosch-si.com> * changed UI behaviour to close assignment popup in case of IncompatibleTargetTypeException Signed-off-by: Robert Sing <robert.sing@bosch-si.com> * added unit test to validate incompatibly specification fix Signed-off-by: Robert Sing <robert.sing@bosch-si.com> * fixed review findings Signed-off-by: Robert Sing <robert.sing@bosch-si.com> * fixed review findings Signed-off-by: Robert Sing <robert.sing@bosch-si.com> * fix potential null pointer Signed-off-by: Robert Sing <robert.sing@bosch-si.com> * Fixed rolloutcopy by adding dsTypeId to ProxyDistributionSetInfo Signed-off-by: Robert Sing <robert.sing@bosch-si.com> * suppressed warning Signed-off-by: Robert Sing <robert.sing@bosch-si.com>
This commit is contained in:
@@ -42,9 +42,10 @@ 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.IncompatibleTargetTypeException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||
@@ -256,14 +257,14 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
|
||||
private void checkCompatibilityForSingleDsAssignment(final Long distSetId, final List<String> controllerIds) {
|
||||
final DistributionSetType distSetType = distributionSetManagement.getValidAndComplete(distSetId).getType();
|
||||
final Set<Long> incompatibleTargetTypes = Lists.partition(controllerIds, Constants.MAX_ENTRIES_IN_STATEMENT)
|
||||
final Set<String> 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());
|
||||
.flatMap(List::stream).map(Target::getTargetType).map(TargetType::getName).collect(Collectors.toSet());
|
||||
|
||||
if (!incompatibleTargetTypes.isEmpty()) {
|
||||
throw new DistributionSetTypeNotInTargetTypeException(distSetType.getId(), incompatibleTargetTypes);
|
||||
throw new IncompatibleTargetTypeException(incompatibleTargetTypes, distSetType.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,9 +279,9 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
incompatibleDistSetTypes.removeAll(target.getTargetType().getCompatibleDistributionSetTypes());
|
||||
|
||||
if (!incompatibleDistSetTypes.isEmpty()) {
|
||||
final Set<Long> distSetTypeIds = incompatibleDistSetTypes.stream().map(DistributionSetType::getId)
|
||||
final Set<String> distSetTypeNames = incompatibleDistSetTypes.stream().map(DistributionSetType::getName)
|
||||
.collect(Collectors.toSet());
|
||||
throw new DistributionSetTypeNotInTargetTypeException(distSetTypeIds, target.getTargetType().getId());
|
||||
throw new IncompatibleTargetTypeException(target.getTargetType().getName(), distSetTypeNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,25 +10,24 @@ package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import static org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate.addSuccessAndErrorConditionsAndActions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.validation.ConstraintDeclarationException;
|
||||
import javax.validation.ValidationException;
|
||||
|
||||
import org.eclipse.hawkbit.repository.AbstractRolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
|
||||
import org.eclipse.hawkbit.repository.RolloutExecutor;
|
||||
import org.eclipse.hawkbit.repository.RolloutFields;
|
||||
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
||||
import org.eclipse.hawkbit.repository.RolloutHelper;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.RolloutStatusCache;
|
||||
@@ -54,6 +53,7 @@ import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.WeightValidationHelper;
|
||||
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
@@ -70,7 +70,6 @@ import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -96,7 +95,7 @@ import com.google.common.collect.Lists;
|
||||
*/
|
||||
@Validated
|
||||
@Transactional(readOnly = true)
|
||||
public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
public class JpaRolloutManagement implements RolloutManagement {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(JpaRolloutManagement.class);
|
||||
|
||||
private static final List<RolloutStatus> ACTIVE_ROLLOUTS = Arrays.asList(RolloutStatus.CREATING,
|
||||
@@ -125,23 +124,35 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
@Autowired
|
||||
private RolloutStatusCache rolloutStatusCache;
|
||||
|
||||
private final TargetManagement targetManagement;
|
||||
private final DistributionSetManagement distributionSetManagement;
|
||||
private final VirtualPropertyReplacer virtualPropertyReplacer;
|
||||
private final PlatformTransactionManager txManager;
|
||||
private final TenantAware tenantAware;
|
||||
private final LockRegistry lockRegistry;
|
||||
private final RolloutApprovalStrategy rolloutApprovalStrategy;
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final RolloutExecutor rolloutExecutor;
|
||||
|
||||
private final EventPublisherHolder eventPublisherHolder;
|
||||
|
||||
private final Database database;
|
||||
|
||||
public JpaRolloutManagement(final TargetManagement targetManagement,
|
||||
final DeploymentManagement deploymentManagement, final RolloutGroupManagement rolloutGroupManagement,
|
||||
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
|
||||
final EventPublisherHolder eventPublisherHolder, final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||
final PlatformTransactionManager txManager, final TenantAware tenantAware, final LockRegistry lockRegistry,
|
||||
final Database database, final RolloutApprovalStrategy rolloutApprovalStrategy,
|
||||
final DistributionSetManagement distributionSetManagement, final EventPublisherHolder eventPublisherHolder,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager,
|
||||
final TenantAware tenantAware, final LockRegistry lockRegistry, final Database database,
|
||||
final RolloutApprovalStrategy rolloutApprovalStrategy,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement,
|
||||
final SystemSecurityContext systemSecurityContext, final RolloutExecutor rolloutExecutor) {
|
||||
super(targetManagement, deploymentManagement, rolloutGroupManagement, distributionSetManagement, context,
|
||||
virtualPropertyReplacer, txManager, tenantAware, lockRegistry, rolloutApprovalStrategy,
|
||||
tenantConfigurationManagement, systemSecurityContext);
|
||||
this.targetManagement = targetManagement;
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.virtualPropertyReplacer = virtualPropertyReplacer;
|
||||
this.txManager = txManager;
|
||||
this.tenantAware = tenantAware;
|
||||
this.lockRegistry = lockRegistry;
|
||||
this.rolloutApprovalStrategy = rolloutApprovalStrategy;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.eventPublisherHolder = eventPublisherHolder;
|
||||
this.database = database;
|
||||
this.rolloutExecutor = rolloutExecutor;
|
||||
@@ -203,7 +214,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
|
||||
private JpaRollout createRollout(final JpaRollout rollout) {
|
||||
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(rollout);
|
||||
final Long totalTargets = targetManagement.countByRsql(rollout.getTargetFilterQuery());
|
||||
final Long totalTargets = targetManagement.countByRsqlAndCompatible(rollout.getTargetFilterQuery(),
|
||||
rollout.getDistributionSet().getType().getId());
|
||||
if (totalTargets == 0) {
|
||||
throw new ValidationException("Rollout does not match any existing targets");
|
||||
}
|
||||
@@ -248,6 +260,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
final RolloutGroupConditions conditions, final Rollout rollout) {
|
||||
RolloutHelper.verifyRolloutInStatus(rollout, RolloutStatus.CREATING);
|
||||
final JpaRollout savedRollout = (JpaRollout) rollout;
|
||||
final DistributionSetType distributionSetType = savedRollout.getDistributionSet().getType();
|
||||
|
||||
// prepare the groups
|
||||
final List<RolloutGroup> groups = groupList.stream()
|
||||
@@ -255,13 +268,13 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
.collect(Collectors.toList());
|
||||
groups.forEach(RolloutHelper::verifyRolloutGroupHasConditions);
|
||||
|
||||
RolloutHelper.verifyRemainingTargets(
|
||||
calculateRemainingTargets(groups, savedRollout.getTargetFilterQuery(), savedRollout.getCreatedAt()));
|
||||
RolloutHelper.verifyRemainingTargets(calculateRemainingTargets(groups, savedRollout.getTargetFilterQuery(),
|
||||
savedRollout.getCreatedAt(), distributionSetType.getId()));
|
||||
|
||||
// check if we need to enforce the 'max targets per group' quota
|
||||
if (quotaManagement.getMaxTargetsPerRolloutGroup() > 0) {
|
||||
validateTargetsInGroups(groups, savedRollout.getTargetFilterQuery(), savedRollout.getCreatedAt())
|
||||
.getTargetsPerGroup().forEach(this::assertTargetsPerRolloutGroupQuota);
|
||||
validateTargetsInGroups(groups, savedRollout.getTargetFilterQuery(), savedRollout.getCreatedAt(),
|
||||
distributionSetType.getId()).getTargetsPerGroup().forEach(this::assertTargetsPerRolloutGroupQuota);
|
||||
}
|
||||
|
||||
// create and persist the groups (w/o filling them with targets)
|
||||
@@ -299,21 +312,6 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
new RolloutGroupCreatedEvent(group, rollout.getId(), eventPublisherHolder.getApplicationId())));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Async
|
||||
public ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups(final List<RolloutGroupCreate> groups,
|
||||
final String targetFilter, final Long createdAt) {
|
||||
|
||||
final String baseFilter = RolloutHelper.getTargetFilterQuery(targetFilter, createdAt);
|
||||
final long totalTargets = targetManagement.countByRsql(baseFilter);
|
||||
if (totalTargets == 0) {
|
||||
throw new ConstraintDeclarationException("Rollout target filter does not match any targets");
|
||||
}
|
||||
|
||||
return new AsyncResult<>(validateTargetsInGroups(
|
||||
groups.stream().map(RolloutGroupCreate::build).collect(Collectors.toList()), baseFilter, totalTargets));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(include = {
|
||||
@@ -524,7 +522,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
|
||||
@Override
|
||||
public Page<Rollout> findAllWithDetailedStatus(final Pageable pageable, final boolean deleted) {
|
||||
Page<JpaRollout> rollouts;
|
||||
final Page<JpaRollout> rollouts;
|
||||
final Specification<JpaRollout> spec = RolloutSpecification.isDeletedWithDistributionSet(deleted);
|
||||
rollouts = rolloutRepository.findAll(spec, pageable);
|
||||
setRolloutStatusDetails(rollouts);
|
||||
@@ -599,8 +597,6 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
* Enforces the quota defining the maximum number of {@link Target}s per
|
||||
* {@link RolloutGroup}.
|
||||
*
|
||||
* @param group
|
||||
* The rollout group
|
||||
* @param requested
|
||||
* number of targets to check
|
||||
*/
|
||||
@@ -626,4 +622,93 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
});
|
||||
}
|
||||
|
||||
private RolloutGroupsValidation validateTargetsInGroups(final List<RolloutGroup> groups, final String baseFilter,
|
||||
final long totalTargets, final Long dsTypeId) {
|
||||
final List<Long> groupTargetCounts = new ArrayList<>(groups.size());
|
||||
final Map<String, Long> targetFilterCounts = groups.stream()
|
||||
.map(group -> RolloutHelper.getGroupTargetFilter(baseFilter, group)).distinct()
|
||||
.collect(Collectors.toMap(Function.identity(),
|
||||
groupTargetFilter -> targetManagement.countByRsqlAndCompatible(groupTargetFilter, dsTypeId)));
|
||||
|
||||
long unusedTargetsCount = 0;
|
||||
|
||||
for (int i = 0; i < groups.size(); i++) {
|
||||
final RolloutGroup group = groups.get(i);
|
||||
final String groupTargetFilter = RolloutHelper.getGroupTargetFilter(baseFilter, group);
|
||||
RolloutHelper.verifyRolloutGroupTargetPercentage(group.getTargetPercentage());
|
||||
|
||||
final long targetsInGroupFilter = targetFilterCounts.get(groupTargetFilter);
|
||||
final long overlappingTargets = countOverlappingTargetsWithPreviousGroups(baseFilter, groups, group, i,
|
||||
targetFilterCounts);
|
||||
|
||||
final long realTargetsInGroup;
|
||||
// Assume that targets which were not used in the previous groups
|
||||
// are used in this group
|
||||
if (overlappingTargets > 0 && unusedTargetsCount > 0) {
|
||||
realTargetsInGroup = targetsInGroupFilter - overlappingTargets + unusedTargetsCount;
|
||||
unusedTargetsCount = 0;
|
||||
} else {
|
||||
realTargetsInGroup = targetsInGroupFilter - overlappingTargets;
|
||||
}
|
||||
|
||||
final long reducedTargetsInGroup = Math
|
||||
.round(group.getTargetPercentage() / 100 * (double) realTargetsInGroup);
|
||||
groupTargetCounts.add(reducedTargetsInGroup);
|
||||
unusedTargetsCount += realTargetsInGroup - reducedTargetsInGroup;
|
||||
|
||||
}
|
||||
|
||||
return new RolloutGroupsValidation(totalTargets, groupTargetCounts);
|
||||
}
|
||||
|
||||
private long countOverlappingTargetsWithPreviousGroups(final String baseFilter, final List<RolloutGroup> groups,
|
||||
final RolloutGroup group, final int groupIndex, final Map<String, Long> targetFilterCounts) {
|
||||
// there can't be overlapping targets in the first group
|
||||
if (groupIndex == 0) {
|
||||
return 0;
|
||||
}
|
||||
final List<RolloutGroup> previousGroups = groups.subList(0, groupIndex);
|
||||
final String overlappingTargetsFilter = RolloutHelper.getOverlappingWithGroupsTargetFilter(baseFilter,
|
||||
previousGroups, group);
|
||||
|
||||
if (targetFilterCounts.containsKey(overlappingTargetsFilter)) {
|
||||
return targetFilterCounts.get(overlappingTargetsFilter);
|
||||
} else {
|
||||
final long overlappingTargets = targetManagement.countByRsql(overlappingTargetsFilter);
|
||||
targetFilterCounts.put(overlappingTargetsFilter, overlappingTargets);
|
||||
return overlappingTargets;
|
||||
}
|
||||
}
|
||||
|
||||
private long calculateRemainingTargets(final List<RolloutGroup> groups, final String targetFilter,
|
||||
final Long createdAt, final Long dsTypeId) {
|
||||
final String baseFilter = RolloutHelper.getTargetFilterQuery(targetFilter, createdAt);
|
||||
final long totalTargets = targetManagement.countByRsqlAndCompatible(baseFilter, dsTypeId);
|
||||
if (totalTargets == 0) {
|
||||
throw new ConstraintDeclarationException("Rollout target filter does not match any targets");
|
||||
}
|
||||
|
||||
final RolloutGroupsValidation validation = validateTargetsInGroups(groups, baseFilter, totalTargets, dsTypeId);
|
||||
|
||||
return totalTargets - validation.getTargetsInGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Async
|
||||
public ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups(final List<RolloutGroupCreate> groups,
|
||||
final String targetFilter, final Long createdAt, final Long dsTypeId) {
|
||||
|
||||
final String baseFilter = RolloutHelper.getTargetFilterQuery(targetFilter, createdAt);
|
||||
|
||||
final long totalTargets = targetManagement.countByRsqlAndCompatible(baseFilter, dsTypeId);
|
||||
|
||||
if (totalTargets == 0) {
|
||||
throw new ConstraintDeclarationException("Rollout target filter does not match any targets");
|
||||
}
|
||||
|
||||
return new AsyncResult<>(
|
||||
validateTargetsInGroups(groups.stream().map(RolloutGroupCreate::build).collect(Collectors.toList()),
|
||||
baseFilter, totalTargets, dsTypeId));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -786,10 +786,14 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
public long countByRsql(final String targetFilterQuery) {
|
||||
final Specification<JpaTarget> specs = RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class,
|
||||
virtualPropertyReplacer, database);
|
||||
return targetRepository.count((root, query, cb) -> {
|
||||
query.distinct(true);
|
||||
return specs.toPredicate(root, query, cb);
|
||||
});
|
||||
return targetRepository.count(specs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByRsqlAndCompatible(final String targetFilterQuery, final Long dsTypeId) {
|
||||
final Specification<JpaTarget> rsqlSpec = RSQLUtility.buildRsqlSpecification(targetFilterQuery,
|
||||
TargetFields.class, virtualPropertyReplacer, database);
|
||||
return targetRepository.count(rsqlSpec.and(TargetSpecifications.isCompatibleWithDistributionSetType(dsTypeId)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -664,17 +664,15 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
RolloutManagement rolloutManagement(final TargetManagement targetManagement,
|
||||
final DeploymentManagement deploymentManagement, final RolloutGroupManagement rolloutGroupManagement,
|
||||
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
|
||||
final EventPublisherHolder eventPublisherHolder, final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||
final PlatformTransactionManager txManager, final TenantAware tenantAware, final LockRegistry lockRegistry,
|
||||
final JpaProperties properties, final RolloutApprovalStrategy rolloutApprovalStrategy,
|
||||
final DistributionSetManagement distributionSetManagement, final EventPublisherHolder eventPublisherHolder,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager,
|
||||
final TenantAware tenantAware, final LockRegistry lockRegistry, final JpaProperties properties,
|
||||
final RolloutApprovalStrategy rolloutApprovalStrategy,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement,
|
||||
final SystemSecurityContext systemSecurityContext, final RolloutExecutor rolloutExecutor) {
|
||||
return new JpaRolloutManagement(targetManagement, deploymentManagement, rolloutGroupManagement,
|
||||
distributionSetManagement, context, eventPublisherHolder, virtualPropertyReplacer, txManager,
|
||||
tenantAware, lockRegistry, properties.getDatabase(), rolloutApprovalStrategy,
|
||||
tenantConfigurationManagement, systemSecurityContext, rolloutExecutor);
|
||||
return new JpaRolloutManagement(targetManagement, distributionSetManagement, eventPublisherHolder,
|
||||
virtualPropertyReplacer, txManager, tenantAware, lockRegistry, properties.getDatabase(),
|
||||
rolloutApprovalStrategy, tenantConfigurationManagement, systemSecurityContext, rolloutExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -394,7 +394,7 @@ public final class TargetSpecifications {
|
||||
// isNull predicate first
|
||||
final Predicate targetTypeIsNull = targetRoot.get(JpaTarget_.targetType).isNull();
|
||||
|
||||
return cb.or(targetTypeIsNull, getDistSetTypeEqualPredicate(targetRoot, cb, distributionSetTypeId));
|
||||
return cb.or(targetTypeIsNull, cb.equal(getDsTypeIdPath(targetRoot), distributionSetTypeId));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -414,19 +414,21 @@ public final class TargetSpecifications {
|
||||
// isNotNull predicate first
|
||||
final Predicate targetTypeNotNull = targetRoot.get(JpaTarget_.targetType).isNotNull();
|
||||
|
||||
// We need to check for isNull(...) and notEqual(...) since we allow
|
||||
// target types that don't have any compatible distribution set type
|
||||
return cb.and(targetTypeNotNull,
|
||||
cb.isNull(getDistSetTypeEqualPredicate(targetRoot, cb, distributionSetTypeId)));
|
||||
cb.or(cb.isNull(getDsTypeIdPath(targetRoot)),
|
||||
cb.notEqual(getDsTypeIdPath(targetRoot), distributionSetTypeId)));
|
||||
};
|
||||
}
|
||||
|
||||
private static Predicate getDistSetTypeEqualPredicate(final Root<JpaTarget> root, final CriteriaBuilder cb,
|
||||
final Long dsTypeId) {
|
||||
private static Path<Long> getDsTypeIdPath(final Root<JpaTarget> root) {
|
||||
final Join<JpaTarget, JpaTargetType> targetTypeJoin = root.join(JpaTarget_.targetType, JoinType.LEFT);
|
||||
targetTypeJoin.fetch(JpaTargetType_.distributionSetTypes);
|
||||
final SetJoin<JpaTargetType, JpaDistributionSetType> dsTypeTargetTypeJoin = targetTypeJoin
|
||||
.join(JpaTargetType_.distributionSetTypes, JoinType.LEFT);
|
||||
|
||||
return cb.equal(dsTypeTargetTypeJoin.get(JpaDistributionSetType_.id), dsTypeId);
|
||||
return dsTypeTargetTypeJoin.get(JpaDistributionSetType_.id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,9 +41,9 @@ 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.IncompatibleTargetTypeException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
|
||||
@@ -62,6 +62,7 @@ import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
@@ -1442,14 +1443,31 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
@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 DistributionSetType dsType = testdataFactory.findOrCreateDistributionSetType("test-ds-type", "dsType");
|
||||
final TargetType targetType = testdataFactory.createTargetType("target-type",
|
||||
Collections.singletonList(dsType));
|
||||
final Target target = testdataFactory.createTarget("test-target", "test-target", targetType.getId());
|
||||
|
||||
final DeploymentRequest deploymentRequest = DeploymentManagement
|
||||
.deploymentRequest(target.getControllerId(), ds.getId()).build();
|
||||
final List<DeploymentRequest> deploymentRequests = Collections.singletonList(deploymentRequest);
|
||||
|
||||
assertThatExceptionOfType(DistributionSetTypeNotInTargetTypeException.class)
|
||||
assertThatExceptionOfType(IncompatibleTargetTypeException.class)
|
||||
.isThrownBy(() -> deploymentManagement.assignDistributionSets(deploymentRequests));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that the DistributionSet assignment fails for target with target type that is not compatible with any dsType.")
|
||||
void verifyDSAssignmentFailsForTargetsWithTargetTypesThatAreNotCompatibleWithAnyDs() {
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet("test-ds");
|
||||
final TargetType emptyTargetType = testdataFactory.createTargetType("target-type", Collections.emptyList());
|
||||
final Target targetWithEmptyType = testdataFactory.createTarget("test-target", "test-target",
|
||||
emptyTargetType.getId());
|
||||
|
||||
final DeploymentRequest deploymentRequestWithEmptyType = DeploymentManagement
|
||||
.deploymentRequest(targetWithEmptyType.getControllerId(), ds.getId()).build();
|
||||
final List<DeploymentRequest> deploymentRequests = Collections.singletonList(deploymentRequestWithEmptyType);
|
||||
assertThatExceptionOfType(IncompatibleTargetTypeException.class)
|
||||
.isThrownBy(() -> deploymentManagement.assignDistributionSets(deploymentRequests));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user