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:
@@ -181,8 +181,8 @@ public interface RolloutManagement {
|
||||
RolloutGroupConditions conditions);
|
||||
|
||||
/**
|
||||
* Calculates how many targets are addressed by each rollout group and
|
||||
* returns the validation information.
|
||||
* Calculates how many targets are addressed by each rollout group and returns
|
||||
* the validation information.
|
||||
*
|
||||
* @param groups
|
||||
* a list of rollout groups
|
||||
@@ -190,6 +190,8 @@ public interface RolloutManagement {
|
||||
* the rollout
|
||||
* @param createdAt
|
||||
* timestamp when the rollout was created
|
||||
* @param dsTypeId
|
||||
* ID of the type of distribution set of the rollout
|
||||
* @return the validation information
|
||||
* @throws RolloutIllegalStateException
|
||||
* thrown when no targets are targeted by the rollout
|
||||
@@ -199,7 +201,7 @@ public interface RolloutManagement {
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ_AND_TARGET_READ)
|
||||
ListenableFuture<RolloutGroupsValidation> validateTargetsInGroups(@Valid List<RolloutGroupCreate> groups,
|
||||
String targetFilter, Long createdAt);
|
||||
String targetFilter, Long createdAt, @NotNull Long dsTypeId);
|
||||
|
||||
/**
|
||||
* Retrieves all rollouts.
|
||||
|
||||
@@ -102,8 +102,8 @@ public interface TargetManagement {
|
||||
* @param selectTargetWithNoTag
|
||||
* flag to select targets with no tag assigned
|
||||
*
|
||||
* @return the found number {@link Target}s
|
||||
*
|
||||
* @return the found number of {@link Target}s
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* if distribution set with given ID does not exist
|
||||
*/
|
||||
@@ -117,7 +117,7 @@ public interface TargetManagement {
|
||||
* @param distId
|
||||
* to search for
|
||||
* @return number of found {@link Target}s.
|
||||
*
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* if distribution set with given ID does not exist
|
||||
*/
|
||||
@@ -144,19 +144,33 @@ public interface TargetManagement {
|
||||
*
|
||||
* @param rsqlParam
|
||||
* filter definition in RSQL syntax
|
||||
* @return the found number {@link Target}s
|
||||
* @return the found number of {@link Target}s
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
long countByRsql(@NotEmpty String rsqlParam);
|
||||
|
||||
/**
|
||||
* Count all targets for given {@link TargetFilterQuery} and that are compatible
|
||||
* with the passed {@link DistributionSetType}.
|
||||
*
|
||||
* @param rsqlParam
|
||||
* filter definition in RSQL syntax
|
||||
* @param dsTypeId
|
||||
* ID of the {@link DistributionSetType} the targets need to be
|
||||
* compatible with
|
||||
* @return the found number of{@link Target}s
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
long countByRsqlAndCompatible(@NotEmpty String rsqlParam, @NotNull Long dsTypeId);
|
||||
|
||||
/**
|
||||
* Count {@link TargetFilterQuery}s for given target filter query.
|
||||
*
|
||||
* @param targetFilterQueryId
|
||||
* {@link TargetFilterQuery#getId()}
|
||||
* @return the found number {@link Target}s
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* @return the found number of {@link Target}s
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* if {@link TargetFilterQuery} with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.exception;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
|
||||
/**
|
||||
* the {@link DistributionSetTypeNotInTargetTypeException} is thrown when a
|
||||
* {@link DistributionSetType} is requested as part of a {@link TargetType} but
|
||||
* is not returned in {@link TargetType#getCompatibleDistributionSetTypes()}.
|
||||
*/
|
||||
public class DistributionSetTypeNotInTargetTypeException extends EntityNotFoundException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param distributionSetTypeId
|
||||
* 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 Collection<Long> 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<Long> distributionSetTypeIds,
|
||||
final Long targetTypeId) {
|
||||
super("DistributionSetTypes " + distributionSetTypeIds + " are not compatible to TargetTypes " + targetTypeId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.exception;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
|
||||
/**
|
||||
* Thrown if user tries to assign a {@link DistributionSet} to a {@link Target}
|
||||
* that has an incompatible {@link TargetType}
|
||||
*/
|
||||
public class IncompatibleTargetTypeException extends AbstractServerRtException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final Collection<String> targetTypeNames;
|
||||
private final Collection<String> distributionSetTypeNames;
|
||||
|
||||
/**
|
||||
* Creates a new IncompatibleTargetTypeException with
|
||||
* {@link SpServerError#SP_TARGET_TYPE_INCOMPATIBLE} error.
|
||||
*
|
||||
* @param targetTypeName
|
||||
* Name of the target type
|
||||
* @param distributionSetTypeNames
|
||||
* Names of the distribution set types
|
||||
*/
|
||||
public IncompatibleTargetTypeException(final String targetTypeName,
|
||||
final Collection<String> distributionSetTypeNames) {
|
||||
super(String.format("Target of type %s is not compatible with distribution set of types %s", targetTypeName,
|
||||
distributionSetTypeNames), SpServerError.SP_TARGET_TYPE_INCOMPATIBLE);
|
||||
this.targetTypeNames = Collections.singleton(targetTypeName);
|
||||
this.distributionSetTypeNames = distributionSetTypeNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new IncompatibleTargetTypeException with
|
||||
* {@link SpServerError#SP_TARGET_TYPE_INCOMPATIBLE} error.
|
||||
*
|
||||
* @param targetTypeNames
|
||||
* Name of the target types
|
||||
* @param distributionSetTypeName
|
||||
* Name of the distribution set type
|
||||
*/
|
||||
public IncompatibleTargetTypeException(final Collection<String> targetTypeNames,
|
||||
final String distributionSetTypeName) {
|
||||
super(String.format("Targets of types %s are not compatible with distribution set of type %s", targetTypeNames,
|
||||
distributionSetTypeName), SpServerError.SP_TARGET_TYPE_INCOMPATIBLE);
|
||||
this.targetTypeNames = targetTypeNames;
|
||||
this.distributionSetTypeNames = Collections.singleton(distributionSetTypeName);
|
||||
}
|
||||
|
||||
public Collection<String> getTargetTypeNames() {
|
||||
return targetTypeNames;
|
||||
}
|
||||
|
||||
public Collection<String> getDistributionSetTypeNames() {
|
||||
return distributionSetTypeNames;
|
||||
}
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.validation.ConstraintDeclarationException;
|
||||
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupsValidation;
|
||||
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.integration.support.locks.LockRegistry;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.annotation.AsyncResult;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
/**
|
||||
* Core functionality for {@link RolloutManagement} implementations.
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractRolloutManagement implements RolloutManagement {
|
||||
|
||||
protected final TargetManagement targetManagement;
|
||||
|
||||
protected final DeploymentManagement deploymentManagement;
|
||||
|
||||
protected final RolloutGroupManagement rolloutGroupManagement;
|
||||
|
||||
protected final DistributionSetManagement distributionSetManagement;
|
||||
|
||||
protected final ApplicationContext context;
|
||||
|
||||
protected final VirtualPropertyReplacer virtualPropertyReplacer;
|
||||
|
||||
protected final PlatformTransactionManager txManager;
|
||||
|
||||
protected final TenantAware tenantAware;
|
||||
|
||||
protected final LockRegistry lockRegistry;
|
||||
|
||||
protected final RolloutApprovalStrategy rolloutApprovalStrategy;
|
||||
|
||||
protected final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
|
||||
protected final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
protected AbstractRolloutManagement(final TargetManagement targetManagement,
|
||||
final DeploymentManagement deploymentManagement, final RolloutGroupManagement rolloutGroupManagement,
|
||||
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager,
|
||||
final TenantAware tenantAware, final LockRegistry lockRegistry,
|
||||
final RolloutApprovalStrategy rolloutApprovalStrategy,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement,
|
||||
final SystemSecurityContext systemSecurityContext) {
|
||||
this.targetManagement = targetManagement;
|
||||
this.deploymentManagement = deploymentManagement;
|
||||
this.rolloutGroupManagement = rolloutGroupManagement;
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.context = context;
|
||||
this.virtualPropertyReplacer = virtualPropertyReplacer;
|
||||
this.txManager = txManager;
|
||||
this.tenantAware = tenantAware;
|
||||
this.lockRegistry = lockRegistry;
|
||||
this.rolloutApprovalStrategy = rolloutApprovalStrategy;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
}
|
||||
|
||||
protected RolloutGroupsValidation validateTargetsInGroups(final List<RolloutGroup> groups, final String baseFilter,
|
||||
final long totalTargets) {
|
||||
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(), targetManagement::countByRsql));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
protected long calculateRemainingTargets(final List<RolloutGroup> 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");
|
||||
}
|
||||
|
||||
final RolloutGroupsValidation validation = validateTargetsInGroups(groups, baseFilter, totalTargets);
|
||||
|
||||
return totalTargets - validation.getTargetsInGroups();
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
}
|
||||
@@ -45,8 +45,8 @@ public final class RolloutHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the group has the required success condition and action and
|
||||
* a falid target percentage.
|
||||
* Verifies that the group has the required success condition and action and a
|
||||
* valid target percentage.
|
||||
*
|
||||
* @param group
|
||||
* the input group
|
||||
@@ -140,8 +140,8 @@ public final class RolloutHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the groups of a Rollout to match a specific status and adds a
|
||||
* group to the result.
|
||||
* Filters the groups of a Rollout to match a specific status and adds a group
|
||||
* to the result.
|
||||
*
|
||||
* @param status
|
||||
* the required status for the groups
|
||||
@@ -156,8 +156,8 @@ public final class RolloutHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an RSQL expression that matches all targets in the provided
|
||||
* groups. Links all target filter queries with OR.
|
||||
* Creates an RSQL expression that matches all targets in the provided groups.
|
||||
* Links all target filter queries with OR.
|
||||
*
|
||||
* @param groups
|
||||
* the rollout groups
|
||||
@@ -224,7 +224,7 @@ public final class RolloutHelper {
|
||||
* group for which the filter string should be created
|
||||
* @return the final target filter query for a rollout group
|
||||
*/
|
||||
static String getGroupTargetFilter(final String baseFilter, final RolloutGroup group) {
|
||||
public static String getGroupTargetFilter(final String baseFilter, final RolloutGroup group) {
|
||||
if (StringUtils.isEmpty(group.getTargetFilterQuery())) {
|
||||
return baseFilter;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -1205,8 +1205,7 @@ public class TestdataFactory {
|
||||
|
||||
/**
|
||||
* Finds {@link TargetType} in repository with given
|
||||
* {@link TargetType#getName()} or creates if it does not exist yet. No ds
|
||||
* types
|
||||
* {@link TargetType#getName()} or creates if it does not exist yet. No ds types
|
||||
* are assigned on creation.
|
||||
*
|
||||
* @param targetTypeName
|
||||
@@ -1222,8 +1221,7 @@ public class TestdataFactory {
|
||||
|
||||
/**
|
||||
* Creates {@link TargetType} in repository with given
|
||||
* {@link TargetType#getName()}. Compatible distribution set types are
|
||||
* assigned
|
||||
* {@link TargetType#getName()}. Compatible distribution set types are assigned
|
||||
* on creation
|
||||
*
|
||||
* @param targetTypeName
|
||||
@@ -1256,8 +1254,8 @@ public class TestdataFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a distribution set and directly invalidates it. No actions will
|
||||
* be canceled and no rollouts will be stopped with this invalidation.
|
||||
* Creates a distribution set and directly invalidates it. No actions will be
|
||||
* canceled and no rollouts will be stopped with this invalidation.
|
||||
*
|
||||
* @return created invalidated {@link DistributionSet}
|
||||
*/
|
||||
@@ -1269,8 +1267,8 @@ public class TestdataFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a distribution set that has no software modules assigned, so it
|
||||
* is incomplete.
|
||||
* Creates a distribution set that has no software modules assigned, so it is
|
||||
* incomplete.
|
||||
*
|
||||
* @return created incomplete {@link DistributionSet}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user