Assign multiple distribution sets to a target via mgmt api (#886)

* Add multiassignment to mgmt api target endpoint
* Remove single assignment ds to targets offline
* Fix tests
* Add quota for maxResultingActionsPerManualAssignment
* Fix assignment with same target or distribution set multiple times in one request
* Log UI error
* Add tests
* Enable single assignment requests with multiple DSs and types
* Remove redundant target to DS assignment methods
* Add tests, fix assignment
* Fix possible nullpointer during target assignment request
* Update api docu
* Clean up deployment management code
* Enforce MaxActions quota for offline assignment
* Fix review findings
* Rename property, add migration into
* Add builder for DeploymentRequest
* Change offline assignment method to accept an assignment list, like online assignment
* Fix PR findings

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>
This commit is contained in:
Stefan Klotz
2019-09-17 14:20:26 +02:00
committed by Stefan Behl
parent dba972423b
commit 8687510131
50 changed files with 1466 additions and 559 deletions

View File

@@ -10,9 +10,9 @@ package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BooleanSupplier;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.QuotaManagement;
@@ -51,17 +51,19 @@ public abstract class AbstractDsAssignmentStrategy {
protected final ActionRepository actionRepository;
private final ActionStatusRepository actionStatusRepository;
private final QuotaManagement quotaManagement;
private final BooleanSupplier multiAssignmentsConfig;
AbstractDsAssignmentStrategy(final TargetRepository targetRepository,
final AfterTransactionCommitExecutor afterCommit, final EventPublisherHolder eventPublisherHolder,
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
final QuotaManagement quotaManagement) {
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig) {
this.targetRepository = targetRepository;
this.afterCommit = afterCommit;
this.eventPublisherHolder = eventPublisherHolder;
this.actionRepository = actionRepository;
this.actionStatusRepository = actionStatusRepository;
this.quotaManagement = quotaManagement;
this.multiAssignmentsConfig = multiAssignmentsConfig;
}
/**
@@ -199,14 +201,14 @@ public abstract class AbstractDsAssignmentStrategy {
new CancelTargetAssignmentEvent(target, actionId, eventPublisherHolder.getApplicationId())));
}
JpaAction createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap, final JpaTarget target,
JpaAction createTargetAction(final TargetWithActionType targetWithActionType, final List<JpaTarget> targets,
final JpaDistributionSet set) {
// enforce the 'max actions per target' quota
assertActionsPerTargetQuota(target, 1);
final Optional<JpaTarget> optTarget = targets.stream()
.filter(t -> t.getControllerId().equals(targetWithActionType.getControllerId())).findFirst();
// create the action
return getTargetWithActionType(targetsWithActionMap, target.getControllerId()).map(targetWithActionType -> {
return optTarget.map(target -> {
assertActionsPerTargetQuota(target, 1);
final JpaAction actionForTarget = new JpaAction();
actionForTarget.setActionType(targetWithActionType.getActionType());
actionForTarget.setForcedTime(targetWithActionType.getForceTime());
@@ -218,7 +220,7 @@ public abstract class AbstractDsAssignmentStrategy {
actionForTarget.setMaintenanceWindowTimeZone(targetWithActionType.getMaintenanceWindowTimeZone());
return actionForTarget;
}).orElseGet(() -> {
LOG.warn("Cannot find targetWithActionType for target '{}'.", target.getControllerId());
LOG.warn("Cannot find target for targetWithActionType '{}'.", targetWithActionType.getControllerId());
return null;
});
}
@@ -241,14 +243,7 @@ public abstract class AbstractDsAssignmentStrategy {
actionRepository::countByTargetId);
}
private static Optional<TargetWithActionType> getTargetWithActionType(
final Map<String, TargetWithActionType> targetsWithActionMap, final String controllerId) {
if (targetsWithActionMap.containsKey(controllerId)) {
return Optional.of(targetsWithActionMap.get(controllerId));
} else {
return targetsWithActionMap.values().stream()
.filter(t -> controllerId.equalsIgnoreCase(t.getControllerId())).findFirst();
}
protected boolean isMultiAssignmentsEnabled() {
return multiAssignmentsConfig.getAsBoolean();
}
}

View File

@@ -18,6 +18,7 @@ import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@@ -43,6 +44,7 @@ import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException;
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;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
@@ -59,6 +61,7 @@ import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
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.DistributionSetType;
@@ -81,8 +84,12 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@@ -131,6 +138,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
private final SystemSecurityContext systemSecurityContext;
private final TenantAware tenantAware;
private final Database database;
private final RetryTemplate retryTemplate;
protected JpaDeploymentManagement(final EntityManager entityManager, final ActionRepository actionRepository,
final DistributionSetRepository distributionSetRepository, final TargetRepository targetRepository,
@@ -150,80 +158,88 @@ public class JpaDeploymentManagement implements DeploymentManagement {
onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, afterCommit, eventPublisherHolder,
actionRepository, actionStatusRepository, quotaManagement, this::isMultiAssignmentsEnabled);
offlineDsAssignmentStrategy = new OfflineDsAssignmentStrategy(targetRepository, afterCommit,
eventPublisherHolder, actionRepository, actionStatusRepository, quotaManagement);
eventPublisherHolder, actionRepository, actionStatusRepository, quotaManagement,
this::isMultiAssignmentsEnabled);
this.tenantConfigurationManagement = tenantConfigurationManagement;
this.quotaManagement = quotaManagement;
this.systemSecurityContext = systemSecurityContext;
this.tenantAware = tenantAware;
this.database = database;
retryTemplate = createRetryTemplate();
}
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetAssignmentResult offlineAssignedDistributionSet(final Long dsID,
final Collection<String> controllerIDs) {
final DistributionSetAssignmentResult result = assignDistributionSetToTargets(dsID,
controllerIDs.stream()
.map(controllerId -> new TargetWithActionType(controllerId, ActionType.FORCED, -1))
.collect(Collectors.toList()),
null, offlineDsAssignmentStrategy);
offlineDsAssignmentStrategy.sendDeploymentEvents(result);
return result;
}
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetAssignmentResult assignDistributionSet(final long dsID, final ActionType actionType,
final long forcedTimestamp, final Collection<String> controllerIDs) {
final DistributionSetAssignmentResult result = assignDistributionSetToTargets(dsID,
controllerIDs.stream()
.map(controllerId -> new TargetWithActionType(controllerId, actionType, forcedTimestamp))
.collect(Collectors.toList()),
null, onlineDsAssignmentStrategy);
onlineDsAssignmentStrategy.sendDeploymentEvents(result);
return result;
}
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetAssignmentResult assignDistributionSet(final long dsID,
final Collection<TargetWithActionType> targets) {
final DistributionSetAssignmentResult result = assignDistributionSetToTargets(dsID, targets, null,
onlineDsAssignmentStrategy);
onlineDsAssignmentStrategy.sendDeploymentEvents(result);
return result;
}
@Override
public List<DistributionSetAssignmentResult> assignDistributionSets(final Set<Long> dsIDs,
final Collection<TargetWithActionType> targets) {
final List<DistributionSetAssignmentResult> results = dsIDs.stream()
.map(dsID -> assignDistributionSetToTargets(dsID, targets, null, onlineDsAssignmentStrategy))
public List<DistributionSetAssignmentResult> offlineAssignedDistributionSets(
final Collection<Entry<String, Long>> assignments) {
final Collection<Entry<String, Long>> distinctAssignments = assignments.stream().distinct()
.collect(Collectors.toList());
onlineDsAssignmentStrategy.sendDeploymentEvents(results);
enforceMaxAssignmentsPerRequest(distinctAssignments.size());
final List<DeploymentRequest> deploymentRequests = distinctAssignments.stream()
.map(entry -> DeploymentManagement.deploymentRequest(entry.getKey(), entry.getValue()).build())
.collect(Collectors.toList());
return assignDistributionSets(deploymentRequests, null, offlineDsAssignmentStrategy);
}
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
public List<DistributionSetAssignmentResult> assignDistributionSets(
final List<DeploymentRequest> deploymentRequests) {
return assignDistributionSets(deploymentRequests, null);
}
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
public List<DistributionSetAssignmentResult> assignDistributionSets(
final List<DeploymentRequest> deploymentRequests, final String actionMessage) {
return assignDistributionSets(deploymentRequests, actionMessage, onlineDsAssignmentStrategy);
}
private List<DistributionSetAssignmentResult> assignDistributionSets(
final List<DeploymentRequest> deploymentRequests, final String actionMessage,
final AbstractDsAssignmentStrategy strategy) {
final List<DeploymentRequest> validatedRequests = validateRequestForAssignments(deploymentRequests);
final Map<Long, List<TargetWithActionType>> assignmentsByDsIds = convertRequest(validatedRequests);
final List<DistributionSetAssignmentResult> results = assignmentsByDsIds.entrySet().stream()
.map(entry -> assignDistributionSetToTargetsWithRetry(entry.getKey(), entry.getValue(), actionMessage,
strategy))
.collect(Collectors.toList());
strategy.sendDeploymentEvents(results);
return results;
}
@Override
@Transactional(isolation = Isolation.READ_COMMITTED)
@Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSetAssignmentResult assignDistributionSet(final long dsID,
final Collection<TargetWithActionType> targets, final String actionMessage) {
private List<DeploymentRequest> validateRequestForAssignments(List<DeploymentRequest> deploymentRequests) {
if (!isMultiAssignmentsEnabled()) {
deploymentRequests = deploymentRequests.stream().distinct().collect(Collectors.toList());
checkIfRequiresMultiAssignment(deploymentRequests);
}
checkQuotaForAssignment(deploymentRequests);
return deploymentRequests;
}
final DistributionSetAssignmentResult result = assignDistributionSetToTargets(dsID, targets, actionMessage,
onlineDsAssignmentStrategy);
onlineDsAssignmentStrategy.sendDeploymentEvents(result);
return result;
private static Map<Long, List<TargetWithActionType>> convertRequest(
final Collection<DeploymentRequest> deploymentRequests) {
return deploymentRequests.stream().collect(Collectors.groupingBy(DeploymentRequest::getDistributionSetId,
Collectors.mapping(DeploymentRequest::getTargetWithActionType, Collectors.toList())));
}
private static void checkIfRequiresMultiAssignment(final Collection<DeploymentRequest> deploymentRequests) {
final long distinctTargetsInRequest = deploymentRequests.stream()
.map(request -> request.getTargetWithActionType().getControllerId()).distinct().count();
if (distinctTargetsInRequest < deploymentRequests.size()) {
throw new MultiAssignmentIsNotEnabledException();
}
}
private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final Long dsID,
final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
final AbstractDsAssignmentStrategy assignmentStrategy) {
final RetryCallback<DistributionSetAssignmentResult, ConcurrencyFailureException> retryCallback = retryContext -> assignDistributionSetToTargets(
dsID, targetsWithActionType, actionMessage, assignmentStrategy);
return retryTemplate.execute(retryCallback);
}
/**
@@ -259,10 +275,10 @@ public class JpaDeploymentManagement implements DeploymentManagement {
final AbstractDsAssignmentStrategy assignmentStrategy) {
final JpaDistributionSet distributionSetEntity = getAndValidateDsById(dsID);
checkQuotaForAssignment(targetsWithActionType, distributionSetEntity);
final List<String> targetIds = targetsWithActionType.stream().map(TargetWithActionType::getControllerId).distinct()
.collect(Collectors.toList());
final List<JpaTarget> targetEntities = assignmentStrategy.findTargetsForAssignment(
targetsWithActionType.stream().map(TargetWithActionType::getControllerId).collect(Collectors.toList()),
final List<JpaTarget> targetEntities = assignmentStrategy.findTargetsForAssignment(targetIds,
distributionSetEntity.getId());
if (targetEntities.isEmpty()) {
@@ -288,7 +304,10 @@ public class JpaDeploymentManagement implements DeploymentManagement {
final AbstractDsAssignmentStrategy assignmentStrategy, final JpaDistributionSet distributionSetEntity,
final List<JpaTarget> targetEntities) {
final List<List<Long>> targetEntitiesIdsChunks = getTargetEntitiesAsChunks(targetEntities);
closeOrCancelActiveActions(assignmentStrategy, targetEntitiesIdsChunks);
if (!isMultiAssignmentsEnabled()) {
closeOrCancelActiveActions(assignmentStrategy, targetEntitiesIdsChunks);
}
// cancel all scheduled actions which are in-active, these actions were
// not active before and the manual assignment which has been done
// cancels them
@@ -320,7 +339,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
private static DistributionSetAssignmentResult buildAssignmentResult(final JpaDistributionSet distributionSet,
final List<JpaAction> assignedActions, final int totalTargetsForAssignment) {
int alreadyAssignedTargetsCount = totalTargetsForAssignment - assignedActions.size();
final int alreadyAssignedTargetsCount = totalTargetsForAssignment - assignedActions.size();
return new DistributionSetAssignmentResult(distributionSet, alreadyAssignedTargetsCount, assignedActions);
}
@@ -337,37 +356,31 @@ public class JpaDeploymentManagement implements DeploymentManagement {
return distributionSet;
}
private void checkQuotaForAssignment(final Collection<TargetWithActionType> targetsWithActionType,
final JpaDistributionSet distributionSet) {
// enforce the 'max targets per manual assignment' quota
if (!targetsWithActionType.isEmpty()) {
assertMaxTargetsPerManualAssignmentQuota(distributionSet.getId(), targetsWithActionType.size());
private void checkQuotaForAssignment(final Collection<DeploymentRequest> deploymentRequests) {
if (!deploymentRequests.isEmpty()) {
enforceMaxAssignmentsPerRequest(deploymentRequests.size());
enforceMaxActionsPerTarget(deploymentRequests);
}
}
/**
* Enforces the quota defining the maximum number of {@link Target}s per
* manual {@link DistributionSet} assignment.
*
* @param id
* of the distribution set
* @param requested
* number of targets to check
*/
private void assertMaxTargetsPerManualAssignmentQuota(final Long distributionSetId,
final int requestedTargetsCount) {
QuotaHelper.assertAssignmentQuota(distributionSetId, requestedTargetsCount,
quotaManagement.getMaxTargetsPerManualAssignment(), Target.class, DistributionSet.class, null);
private void enforceMaxAssignmentsPerRequest(final int requestedActions) {
QuotaHelper.assertAssignmentRequestSizeQuota(requestedActions,
quotaManagement.getMaxTargetDistributionSetAssignmentsPerManualAssignment());
}
private void enforceMaxActionsPerTarget(final Collection<DeploymentRequest> deploymentRequests) {
final int quota = quotaManagement.getMaxActionsPerTarget();
final Map<String, Long> countOfTargtInRequest = deploymentRequests.stream()
.map(DeploymentRequest::getControllerId)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
countOfTargtInRequest.forEach((controllerId, count) -> QuotaHelper.assertAssignmentQuota(controllerId, count,
quota, Action.class, Target.class, actionRepository::countByTargetControllerId));
}
private void closeOrCancelActiveActions(final AbstractDsAssignmentStrategy assignmentStrategy,
final List<List<Long>> targetIdsChunks) {
if (isMultiAssignmentsEnabled()) {
LOG.debug("Multi Assignments feature is enabled: No need to close /cancel active actions.");
return;
}
if (isActionsAutocloseEnabled()) {
assignmentStrategy.closeActiveActions(targetIdsChunks);
} else {
@@ -396,10 +409,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
private List<JpaAction> createActions(final Collection<TargetWithActionType> targetsWithActionType,
final List<JpaTarget> targets, final AbstractDsAssignmentStrategy assignmentStrategy,
final JpaDistributionSet set) {
final Map<String, TargetWithActionType> targetsWithActionMap = targetsWithActionType.stream()
.collect(Collectors.toMap(TargetWithActionType::getControllerId, Function.identity()));
return targets.stream().map(trg -> assignmentStrategy.createTargetAction(targetsWithActionMap, trg, set))
return targetsWithActionType.stream().map(twt -> assignmentStrategy.createTargetAction(twt, targets, set))
.filter(Objects::nonNull).map(actionRepository::save).collect(Collectors.toList());
}
@@ -820,4 +831,17 @@ public class JpaDeploymentManagement implements DeploymentManagement {
.runAsSystem(() -> tenantConfigurationManagement.getConfigurationValue(key, valueType).getValue());
}
private static RetryTemplate createRetryTemplate() {
final RetryTemplate template = new RetryTemplate();
final FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
backOffPolicy.setBackOffPeriod(Constants.TX_RT_DELAY);
template.setBackOffPolicy(backOffPolicy);
final SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(Constants.TX_RT_MAX,
Collections.singletonMap(ConcurrencyFailureException.class, true));
template.setRetryPolicy(retryPolicy);
return template;
}
}

View File

@@ -11,8 +11,9 @@ package org.eclipse.hawkbit.repository.jpa;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.QuotaManagement;
@@ -44,9 +45,9 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
OfflineDsAssignmentStrategy(final TargetRepository targetRepository,
final AfterTransactionCommitExecutor afterCommit, final EventPublisherHolder eventPublisherHolder,
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
final QuotaManagement quotaManagement) {
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig) {
super(targetRepository, afterCommit, eventPublisherHolder, actionRepository, actionStatusRepository,
quotaManagement);
quotaManagement, multiAssignmentsConfig);
}
@Override
@@ -59,10 +60,15 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
@Override
public List<JpaTarget> findTargetsForAssignment(final List<String> controllerIDs, final long setId) {
return Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream()
.map(ids -> targetRepository.findAll(SpecificationsBuilder.combineWithAnd(
Arrays.asList(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, setId),
TargetSpecifications.notEqualToTargetUpdateStatus(TargetUpdateStatus.PENDING)))))
final Function<List<String>, List<JpaTarget>> mapper;
if (isMultiAssignmentsEnabled()) {
mapper = targetRepository::findAllByControllerId;
} else {
mapper = ids -> targetRepository.findAll(SpecificationsBuilder.combineWithAnd(
Arrays.asList(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, setId),
TargetSpecifications.notEqualToTargetUpdateStatus(TargetUpdateStatus.PENDING))));
}
return Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream().map(mapper)
.flatMap(List::stream).collect(Collectors.toList());
}
@@ -84,9 +90,9 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
}
@Override
protected JpaAction createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap,
final JpaTarget target, final JpaDistributionSet set) {
final JpaAction result = super.createTargetAction(targetsWithActionMap, target, set);
protected JpaAction createTargetAction(final TargetWithActionType targetWithActionType,
final List<JpaTarget> targets, final JpaDistributionSet set) {
final JpaAction result = super.createTargetAction(targetWithActionType, targets, set);
if (result != null) {
result.setStatus(Status.FINISHED);
result.setActive(Boolean.FALSE);

View File

@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository.jpa;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
@@ -46,15 +45,12 @@ import com.google.common.collect.Lists;
*/
public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
private final BooleanSupplier multiAssignmentsConfig;
OnlineDsAssignmentStrategy(final TargetRepository targetRepository,
final AfterTransactionCommitExecutor afterCommit, final EventPublisherHolder eventPublisherHolder,
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig) {
super(targetRepository, afterCommit, eventPublisherHolder, actionRepository, actionStatusRepository,
quotaManagement);
this.multiAssignmentsConfig = multiAssignmentsConfig;
quotaManagement, multiAssignmentsConfig);
}
@Override
@@ -130,9 +126,9 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
}
@Override
JpaAction createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap, final JpaTarget target,
JpaAction createTargetAction(final TargetWithActionType targetWithActionType, final List<JpaTarget> targets,
final JpaDistributionSet set) {
final JpaAction result = super.createTargetAction(targetsWithActionMap, target, set);
final JpaAction result = super.createTargetAction(targetWithActionType, targets, set);
if (result != null) {
result.setStatus(Status.RUNNING);
}
@@ -207,10 +203,6 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
.publishEvent(new MultiActionEvent(tenant, eventPublisherHolder.getApplicationId(), controllerIds)));
}
private boolean isMultiAssignmentsEnabled() {
return multiAssignmentsConfig.getAsBoolean();
}
private static Stream<Action> filterCancellations(final List<Action> actions) {
return actions.stream().filter(action -> {
final Status actionStatus = action.getStatus();

View File

@@ -19,11 +19,10 @@ import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
@@ -143,11 +142,11 @@ public class AutoAssignChecker {
return DeploymentHelper.runInNewTransaction(transactionManager, "autoAssignDSToTargets",
Isolation.READ_COMMITTED.value(), status -> {
final List<TargetWithActionType> targets = getTargetsWithActionType(targetFilterQuery.getQuery(),
dsId, targetFilterQuery.getAutoAssignActionType(), PAGE_SIZE);
final int count = targets.size();
final List<DeploymentRequest> deploymentRequests = createAssignmentRequests(
targetFilterQuery.getQuery(), dsId, targetFilterQuery.getAutoAssignActionType(), PAGE_SIZE);
final int count = deploymentRequests.size();
if (count > 0) {
deploymentManagement.assignDistributionSet(dsId, targets, actionMessage);
deploymentManagement.assignDistributionSets(deploymentRequests, actionMessage);
}
return count;
});
@@ -168,7 +167,7 @@ public class AutoAssignChecker {
* maximum amount of targets to retrieve
* @return list of targets with action type
*/
private List<TargetWithActionType> getTargetsWithActionType(final String targetFilterQuery, final Long dsId,
private List<DeploymentRequest> createAssignmentRequests(final String targetFilterQuery, final Long dsId,
final ActionType type, final int count) {
final Page<Target> targets = targetManagement.findByTargetFilterQueryAndNonDS(PageRequest.of(0, count), dsId,
targetFilterQuery);
@@ -176,8 +175,10 @@ public class AutoAssignChecker {
// specified)
final ActionType autoAssignActionType = type == null ? ActionType.FORCED : type;
return targets.getContent().stream().map(t -> new TargetWithActionType(t.getControllerId(),
autoAssignActionType, RepositoryModelConstants.NO_FORCE_TIME)).collect(Collectors.toList());
return targets.getContent().stream()
.map(t -> DeploymentManagement.deploymentRequest(t.getControllerId(), dsId)
.setActionType(autoAssignActionType).build())
.collect(Collectors.toList());
}
}

View File

@@ -8,7 +8,7 @@
*/
package org.eclipse.hawkbit.repository.jpa.utils;
import java.util.function.Function;
import java.util.function.ToLongFunction;
import javax.validation.constraints.NotNull;
@@ -76,8 +76,8 @@ public final class QuotaHelper {
* if the assignment operation would cause the quota to be
* exceeded
*/
public static void assertAssignmentQuota(final Long parentId, final long requested, final long limit,
@NotNull final Class<?> type, @NotNull final Class<?> parentType, final Function<Long, Long> countFct) {
public static <T> void assertAssignmentQuota(final T parentId, final long requested, final long limit,
@NotNull final Class<?> type, @NotNull final Class<?> parentType, final ToLongFunction<T> countFct) {
assertAssignmentQuota(parentId, requested, limit, type.getSimpleName(), parentType.getSimpleName(), countFct);
}
@@ -104,8 +104,8 @@ public final class QuotaHelper {
* if the assignment operation would cause the quota to be
* exceeded
*/
public static void assertAssignmentQuota(final Long parentId, final long requested, final long limit,
@NotNull final String type, @NotNull final String parentType, final Function<Long, Long> countFct) {
public static <T> void assertAssignmentQuota(final T parentId, final long requested, final long limit,
@NotNull final String type, @NotNull final String parentType, final ToLongFunction<T> countFct) {
// check if the quota is unlimited
if (limit <= 0) {
@@ -121,7 +121,7 @@ public final class QuotaHelper {
}
if (parentId != null && countFct != null) {
final long currentCount = countFct.apply(parentId);
final long currentCount = countFct.applyAsLong(parentId);
if (currentCount + requested > limit) {
LOG.warn(
"Cannot assign {} {} entities to {} '{}' because of the configured quota limit {}. Currently, there are {} {} entities assigned.",
@@ -130,4 +130,22 @@ public final class QuotaHelper {
}
}
}
/**
* Assert that the number of assignments in a request does not exceed the
* limit.
*
* @param requested
* the number of assignments that are to be made
* @param limit
* the maximum number of assignments per request
*/
public static void assertAssignmentRequestSizeQuota(final long requested, final long limit) {
if (requested > limit) {
final String message = String.format(
"Quota exceeded: Cannot assign %s entities at once. The maximum is %s.", requested, limit);
LOG.warn(message);
throw new QuotaExceededException(message);
}
}
}