Implement TargetFilterQueryManagement with AbstractJpaRepositoryManagement (#2587)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -14,7 +14,6 @@ import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutGroupBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaActionStatusBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupBuilder;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -26,17 +25,20 @@ import org.springframework.validation.annotation.Validated;
|
||||
public class JpaEntityFactory implements EntityFactory {
|
||||
|
||||
private final TargetBuilder targetBuilder;
|
||||
private final TargetFilterQueryBuilder targetFilterQueryBuilder;
|
||||
private final RolloutBuilder rolloutBuilder;
|
||||
|
||||
@SuppressWarnings("java:S107")
|
||||
public JpaEntityFactory(
|
||||
final TargetBuilder targetBuilder, final TargetFilterQueryBuilder targetFilterQueryBuilder,
|
||||
final RolloutBuilder rolloutBuilder) {
|
||||
final TargetBuilder targetBuilder, final RolloutBuilder rolloutBuilder) {
|
||||
this.targetBuilder = targetBuilder;
|
||||
this.targetFilterQueryBuilder = targetFilterQueryBuilder;
|
||||
this.rolloutBuilder = rolloutBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetBuilder target() {
|
||||
return targetBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionStatusBuilder actionStatus() {
|
||||
return new JpaActionStatusBuilder();
|
||||
@@ -51,14 +53,4 @@ public class JpaEntityFactory implements EntityFactory {
|
||||
public RolloutBuilder rollout() {
|
||||
return rolloutBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetBuilder target() {
|
||||
return targetBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetFilterQueryBuilder targetFilterQuery() {
|
||||
return targetFilterQueryBuilder;
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,6 @@ import org.eclipse.hawkbit.repository.artifact.encryption.ArtifactEncryptionServ
|
||||
import org.eclipse.hawkbit.repository.autoassign.AutoAssignExecutor;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
|
||||
import org.eclipse.hawkbit.repository.event.ApplicationEventFilter;
|
||||
import org.eclipse.hawkbit.repository.event.remote.EventEntityManager;
|
||||
import org.eclipse.hawkbit.repository.event.remote.EventEntityManagerHolder;
|
||||
@@ -58,7 +57,6 @@ import org.eclipse.hawkbit.repository.jpa.autocleanup.AutoCleanupScheduler;
|
||||
import org.eclipse.hawkbit.repository.jpa.autocleanup.CleanupTask;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.cluster.DistributedLockRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.cluster.LockProperties;
|
||||
import org.eclipse.hawkbit.repository.jpa.event.JpaEventEntityManager;
|
||||
@@ -316,16 +314,6 @@ public class JpaRepositoryConfiguration {
|
||||
return new JpaRolloutBuilder(distributionSetManagement);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param distributionSetManagement for loading
|
||||
* {@link TargetFilterQuery#getAutoAssignDistributionSet()}
|
||||
* @return TargetFilterQueryBuilder bean
|
||||
*/
|
||||
@Bean
|
||||
TargetFilterQueryBuilder targetFilterQueryBuilder(final DistributionSetManagement<? extends DistributionSet> distributionSetManagement) {
|
||||
return new JpaTargetFilterQueryBuilder(distributionSetManagement);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link SystemSecurityContext} singleton bean which make it
|
||||
* accessible in beans which cannot access the service directly, e.g.
|
||||
@@ -436,10 +424,8 @@ public class JpaRepositoryConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
EntityFactory entityFactory(
|
||||
final TargetBuilder targetBuilder, final TargetFilterQueryBuilder targetFilterQueryBuilder,
|
||||
final RolloutBuilder rolloutBuilder) {
|
||||
return new JpaEntityFactory(targetBuilder, targetFilterQueryBuilder, rolloutBuilder);
|
||||
EntityFactory entityFactory(final TargetBuilder targetBuilder, final RolloutBuilder rolloutBuilder) {
|
||||
return new JpaEntityFactory(targetBuilder, rolloutBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.builder;
|
||||
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
|
||||
import org.eclipse.hawkbit.repository.builder.GenericTargetFilterQueryUpdate;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryBuilder;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryUpdate;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
|
||||
/**
|
||||
* Builder implementation for {@link TargetFilterQuery}.
|
||||
*/
|
||||
public class JpaTargetFilterQueryBuilder implements TargetFilterQueryBuilder {
|
||||
|
||||
private final DistributionSetManagement<? extends DistributionSet> distributionSetManagement;
|
||||
|
||||
public JpaTargetFilterQueryBuilder(final DistributionSetManagement<? extends DistributionSet> distributionSetManagement) {
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetFilterQueryUpdate update(final long id) {
|
||||
return new GenericTargetFilterQueryUpdate(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AutoAssignDistributionSetUpdate updateAutoAssign(final long id) {
|
||||
return new AutoAssignDistributionSetUpdate(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetFilterQueryCreate create() {
|
||||
return new JpaTargetFilterQueryCreate(distributionSetManagement);
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.builder;
|
||||
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.builder.AbstractTargetFilterQueryUpdateCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
|
||||
/**
|
||||
* Create/build implementation.
|
||||
*/
|
||||
public class JpaTargetFilterQueryCreate extends AbstractTargetFilterQueryUpdateCreate<TargetFilterQueryCreate>
|
||||
implements TargetFilterQueryCreate {
|
||||
|
||||
private final DistributionSetManagement<? extends DistributionSet> distributionSetManagement;
|
||||
|
||||
JpaTargetFilterQueryCreate(final DistributionSetManagement<? extends DistributionSet> distributionSetManagement) {
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaTargetFilterQuery build() {
|
||||
return new JpaTargetFilterQuery(name, query,
|
||||
getAutoAssignDistributionSetId().map(distributionSetManagement::getValidAndComplete).orElse(null),
|
||||
getAutoAssignActionType().filter(JpaTargetFilterQueryCreate::isAutoAssignActionTypeValid).orElse(null),
|
||||
weight, getConfirmationRequired().orElse(false));
|
||||
}
|
||||
|
||||
private static boolean isAutoAssignActionTypeValid(final ActionType actionType) {
|
||||
if (!TargetFilterQuery.ALLOWED_AUTO_ASSIGN_ACTION_TYPES.contains(actionType)) {
|
||||
throw new InvalidAutoAssignActionTypeException();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -99,7 +99,6 @@ 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;
|
||||
@@ -122,28 +121,15 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
* Maximum amount of Actions that are started at once.
|
||||
*/
|
||||
private static final int ACTION_PAGE_LIMIT = 1000;
|
||||
private static final String QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED_DEFAULT =
|
||||
"DELETE FROM sp_action " +
|
||||
"WHERE tenant=" + Jpa.nativeQueryParamPrefix() + "tenant" +
|
||||
" AND status IN (%s)" +
|
||||
" AND last_modified_at<" + Jpa.nativeQueryParamPrefix() + "last_modified_at LIMIT " + ACTION_PAGE_LIMIT;
|
||||
private static final String QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED_DEFAULT = "DELETE FROM sp_action " + "WHERE tenant=" + Jpa.nativeQueryParamPrefix() + "tenant" + " AND status IN (%s)" + " AND last_modified_at<" + Jpa.nativeQueryParamPrefix() + "last_modified_at LIMIT " + ACTION_PAGE_LIMIT;
|
||||
private static final EnumMap<Database, String> QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED;
|
||||
|
||||
static {
|
||||
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED = new EnumMap<>(Database.class);
|
||||
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.put(
|
||||
Database.SQL_SERVER,
|
||||
"DELETE TOP (" + ACTION_PAGE_LIMIT + ") FROM sp_action " +
|
||||
"WHERE tenant=" + Jpa.nativeQueryParamPrefix() + "tenant" +
|
||||
" AND status IN (%s)" +
|
||||
" AND last_modified_at<" + Jpa.nativeQueryParamPrefix() + "last_modified_at ");
|
||||
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.put(
|
||||
Database.POSTGRESQL,
|
||||
"DELETE FROM sp_action " +
|
||||
"WHERE id IN (SELECT id FROM sp_action " +
|
||||
"WHERE tenant=" + Jpa.nativeQueryParamPrefix() + "tenant" +
|
||||
" AND status IN (%s)" +
|
||||
" AND last_modified_at<" + Jpa.nativeQueryParamPrefix() + "last_modified_at LIMIT " + ACTION_PAGE_LIMIT + ")");
|
||||
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.put(Database.SQL_SERVER,
|
||||
"DELETE TOP (" + ACTION_PAGE_LIMIT + ") FROM sp_action " + "WHERE tenant=" + Jpa.nativeQueryParamPrefix() + "tenant" + " AND status IN (%s)" + " AND last_modified_at<" + Jpa.nativeQueryParamPrefix() + "last_modified_at ");
|
||||
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.put(Database.POSTGRESQL,
|
||||
"DELETE FROM sp_action " + "WHERE id IN (SELECT id FROM sp_action " + "WHERE tenant=" + Jpa.nativeQueryParamPrefix() + "tenant" + " AND status IN (%s)" + " AND last_modified_at<" + Jpa.nativeQueryParamPrefix() + "last_modified_at LIMIT " + ACTION_PAGE_LIMIT + ")");
|
||||
}
|
||||
|
||||
private final EntityManager entityManager;
|
||||
@@ -161,8 +147,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
private final RetryTemplate retryTemplate;
|
||||
|
||||
@SuppressWarnings("java:S107")
|
||||
protected JpaDeploymentManagement(
|
||||
final EntityManager entityManager, final ActionRepository actionRepository,
|
||||
protected JpaDeploymentManagement(final EntityManager entityManager, final ActionRepository actionRepository,
|
||||
final JpaDistributionSetManagement distributionSetManagement, final TargetRepository targetRepository,
|
||||
final ActionStatusRepository actionStatusRepository, final AuditorAware<String> auditorProvider,
|
||||
final AfterTransactionCommitExecutor afterCommit, final PlatformTransactionManager txManager,
|
||||
@@ -175,12 +160,10 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
this.targetRepository = targetRepository;
|
||||
this.auditorProvider = auditorProvider;
|
||||
this.txManager = txManager;
|
||||
onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, afterCommit,
|
||||
actionRepository, actionStatusRepository, quotaManagement, this::isMultiAssignmentsEnabled,
|
||||
this::isConfirmationFlowEnabled, repositoryProperties);
|
||||
offlineDsAssignmentStrategy = new OfflineDsAssignmentStrategy(targetRepository, afterCommit,
|
||||
actionRepository, actionStatusRepository, quotaManagement,
|
||||
this::isMultiAssignmentsEnabled, this::isConfirmationFlowEnabled, repositoryProperties);
|
||||
onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, afterCommit, actionRepository, actionStatusRepository,
|
||||
quotaManagement, this::isMultiAssignmentsEnabled, this::isConfirmationFlowEnabled, repositoryProperties);
|
||||
offlineDsAssignmentStrategy = new OfflineDsAssignmentStrategy(targetRepository, afterCommit, actionRepository, actionStatusRepository,
|
||||
quotaManagement, this::isMultiAssignmentsEnabled, this::isConfirmationFlowEnabled, repositoryProperties);
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.tenantAware = tenantAware;
|
||||
@@ -197,30 +180,28 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
public List<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
final String initiatedBy, final List<DeploymentRequest> deploymentRequests, final String actionMessage) {
|
||||
public List<DistributionSetAssignmentResult> assignDistributionSets(final String initiatedBy,
|
||||
final List<DeploymentRequest> deploymentRequests, final String actionMessage) {
|
||||
return assignDistributionSets0(initiatedBy, deploymentRequests, actionMessage);
|
||||
}
|
||||
|
||||
private List<DistributionSetAssignmentResult> assignDistributionSets0(
|
||||
final String initiatedBy, final List<DeploymentRequest> deploymentRequests, final String actionMessage) {
|
||||
private List<DistributionSetAssignmentResult> assignDistributionSets0(final String initiatedBy,
|
||||
final List<DeploymentRequest> deploymentRequests, final String actionMessage) {
|
||||
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(deploymentRequests);
|
||||
return assignDistributionSets(initiatedBy, deploymentRequests, actionMessage, onlineDsAssignmentStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DistributionSetAssignmentResult> offlineAssignedDistributionSets(
|
||||
final Collection<Entry<String, Long>> assignments, final String initiatedBy) {
|
||||
public List<DistributionSetAssignmentResult> offlineAssignedDistributionSets(final Collection<Entry<String, Long>> assignments,
|
||||
final String initiatedBy) {
|
||||
final Collection<Entry<String, Long>> distinctAssignments = assignments.stream().distinct().toList();
|
||||
enforceMaxAssignmentsPerRequest(distinctAssignments.size());
|
||||
|
||||
final List<DeploymentRequest> deploymentRequests = distinctAssignments.stream()
|
||||
.map(entry -> DeploymentManagement.deploymentRequest(entry.getKey(), entry.getValue()).build())
|
||||
.toList();
|
||||
.map(entry -> DeploymentManagement.deploymentRequest(entry.getKey(), entry.getValue()).build()).toList();
|
||||
|
||||
return assignDistributionSets(
|
||||
auditorAware.getCurrentAuditor().orElse(tenantAware.getCurrentUsername()),
|
||||
deploymentRequests, null, offlineDsAssignmentStrategy);
|
||||
return assignDistributionSets(auditorAware.getCurrentAuditor().orElse(tenantAware.getCurrentUsername()), deploymentRequests, null,
|
||||
offlineDsAssignmentStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -231,8 +212,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(retryFor = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Action cancelAction(final long actionId) {
|
||||
return cancelAction0(actionId);
|
||||
}
|
||||
@@ -240,8 +221,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
private Action cancelAction0(final long actionId) {
|
||||
log.debug("cancelAction({})", actionId);
|
||||
|
||||
final JpaAction action = actionRepository.findById(actionId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||
final JpaAction action = actionRepository.findById(actionId).orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||
|
||||
if (action.isCancelingOrCanceled()) {
|
||||
throw new CancelActionNotAllowedException("Actions in canceling or canceled state cannot be canceled");
|
||||
@@ -297,8 +277,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
@Override
|
||||
public Optional<Action> findAction(final long actionId) {
|
||||
return actionRepository.findById(actionId)
|
||||
.filter(action -> targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId())))
|
||||
.map(JpaAction.class::cast);
|
||||
.filter(action -> targetRepository.exists(TargetSpecifications.hasId(action.getTarget().getId()))).map(JpaAction.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -326,8 +305,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
@Override
|
||||
public Slice<Action> findActionsByTarget(final String controllerId, final Pageable pageable) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
return actionRepository.findAll(ActionSpecifications.byTargetControllerId(controllerId), pageable)
|
||||
.map(Action.class::cast);
|
||||
return actionRepository.findAll(ActionSpecifications.byTargetControllerId(controllerId), pageable).map(Action.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -357,8 +335,9 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
final CriteriaQuery<String> selMsgQuery = msgQuery.select(join);
|
||||
selMsgQuery.where(cb.equal(as.get(AbstractJpaBaseEntity_.id), actionStatusId));
|
||||
|
||||
final List<String> result = new ArrayList<>(entityManager.createQuery(selMsgQuery)
|
||||
.setFirstResult((int) pageable.getOffset()).setMaxResults(pageable.getPageSize()).getResultList());
|
||||
final List<String> result = new ArrayList<>(
|
||||
entityManager.createQuery(selMsgQuery).setFirstResult((int) pageable.getOffset()).setMaxResults(pageable.getPageSize())
|
||||
.getResultList());
|
||||
|
||||
return new PageImpl<>(result, pageable, result.size());
|
||||
}
|
||||
@@ -372,16 +351,14 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
@Override
|
||||
public Page<Action> findActiveActionsByTarget(final String controllerId, final Pageable pageable) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
return actionRepository
|
||||
.findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, true), pageable)
|
||||
return actionRepository.findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, true), pageable)
|
||||
.map(Action.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Action> findInActiveActionsByTarget(final String controllerId, final Pageable pageable) {
|
||||
assertTargetReadAllowed(controllerId);
|
||||
return actionRepository
|
||||
.findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, false), pageable)
|
||||
return actionRepository.findAll(ActionSpecifications.byTargetControllerIdAndActive(controllerId, false), pageable)
|
||||
.map(Action.class::cast);
|
||||
}
|
||||
|
||||
@@ -393,15 +370,14 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(retryFor = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Action forceQuitAction(final long actionId) {
|
||||
return forceQuitAction0(actionId);
|
||||
}
|
||||
|
||||
private Action forceQuitAction0(final long actionId) {
|
||||
final JpaAction action = actionRepository.findById(actionId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||
final JpaAction action = actionRepository.findById(actionId).orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||
|
||||
if (!action.isCancelingOrCanceled()) {
|
||||
throw new ForceQuitActionNotAllowedException(action.getId() + " is not canceled yet and cannot be force quit");
|
||||
@@ -426,8 +402,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(retryFor = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public Action forceTargetAction(final long actionId) {
|
||||
final JpaAction action = actionRepository.findById(actionId).map(this::assertTargetUpdateAllowed)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
|
||||
@@ -441,8 +417,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
@Retryable(retryFor = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void cancelInactiveScheduledActionsForTargets(final List<Long> targetIds) {
|
||||
if (!isMultiAssignmentsEnabled()) {
|
||||
targetRepository.getAccessController().ifPresent(v -> {
|
||||
@@ -458,38 +434,32 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
|
||||
@Override
|
||||
public void startScheduledActionsByRolloutGroupParent(final long rolloutId, final long distributionSetId, final Long rolloutGroupParentId) {
|
||||
while (DeploymentHelper.runInNewTransaction(
|
||||
txManager,
|
||||
"startScheduledActions-" + rolloutId,
|
||||
status -> {
|
||||
final PageRequest pageRequest = PageRequest.of(0, ACTION_PAGE_LIMIT);
|
||||
final Page<Action> groupScheduledActions;
|
||||
if (rolloutGroupParentId == null) {
|
||||
groupScheduledActions = actionRepository.findByRolloutIdAndRolloutGroupParentIsNullAndStatus(pageRequest, rolloutId,
|
||||
Action.Status.SCHEDULED);
|
||||
} else {
|
||||
groupScheduledActions = actionRepository.findByRolloutIdAndRolloutGroupParentIdAndStatus(pageRequest, rolloutId,
|
||||
rolloutGroupParentId, Action.Status.SCHEDULED);
|
||||
}
|
||||
while (DeploymentHelper.runInNewTransaction(txManager, "startScheduledActions-" + rolloutId, status -> {
|
||||
final PageRequest pageRequest = PageRequest.of(0, ACTION_PAGE_LIMIT);
|
||||
final Page<Action> groupScheduledActions;
|
||||
if (rolloutGroupParentId == null) {
|
||||
groupScheduledActions = actionRepository.findByRolloutIdAndRolloutGroupParentIsNullAndStatus(pageRequest, rolloutId,
|
||||
Action.Status.SCHEDULED);
|
||||
} else {
|
||||
groupScheduledActions = actionRepository.findByRolloutIdAndRolloutGroupParentIdAndStatus(pageRequest, rolloutId,
|
||||
rolloutGroupParentId, Action.Status.SCHEDULED);
|
||||
}
|
||||
|
||||
if (groupScheduledActions.getContent().isEmpty()) {
|
||||
return 0L;
|
||||
} else {
|
||||
// self invocation won't check @PreAuthorize but it is already checked for the method
|
||||
startScheduledActions(groupScheduledActions.getContent());
|
||||
return groupScheduledActions.getTotalElements();
|
||||
}
|
||||
}) > 0) ;
|
||||
if (groupScheduledActions.getContent().isEmpty()) {
|
||||
return 0L;
|
||||
} else {
|
||||
// self invocation won't check @PreAuthorize but it is already checked for the method
|
||||
startScheduledActions(groupScheduledActions.getContent());
|
||||
return groupScheduledActions.getTotalElements();
|
||||
}
|
||||
}) > 0) ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startScheduledActions(final List<Action> rolloutGroupActions) {
|
||||
// Close actions already assigned and collect pending assignments
|
||||
final List<JpaAction> pendingTargetAssignments = rolloutGroupActions.stream()
|
||||
.map(JpaAction.class::cast)
|
||||
.map(this::closeActionIfSetWasAlreadyAssigned)
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
final List<JpaAction> pendingTargetAssignments = rolloutGroupActions.stream().map(JpaAction.class::cast)
|
||||
.map(this::closeActionIfSetWasAlreadyAssigned).filter(Objects::nonNull).toList();
|
||||
if (pendingTargetAssignments.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -524,9 +494,9 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
// the database.
|
||||
final List<Integer> statusList = status.stream().map(Status::ordinal).toList();
|
||||
|
||||
final Query deleteQuery = entityManager.createNativeQuery(String.format(
|
||||
getQueryForDeleteActionsByStatusAndLastModifiedBeforeString(database),
|
||||
Jpa.formatNativeQueryInClause("status", statusList)));
|
||||
final Query deleteQuery = entityManager.createNativeQuery(
|
||||
String.format(getQueryForDeleteActionsByStatusAndLastModifiedBeforeString(database),
|
||||
Jpa.formatNativeQueryInClause("status", statusList)));
|
||||
|
||||
deleteQuery.setParameter("tenant", tenantAware.getCurrentTenant().toUpperCase());
|
||||
Jpa.setNativeQueryInParameter(deleteQuery, "status", statusList);
|
||||
@@ -540,8 +510,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
public boolean hasPendingCancellations(final Long targetId) {
|
||||
// target access checked in assertTargetReadAllowed
|
||||
assertTargetReadAllowed(targetId);
|
||||
return actionRepository
|
||||
.exists(ActionSpecifications.byTargetIdAndIsActiveAndStatus(targetId, Action.Status.CANCELING));
|
||||
return actionRepository.exists(ActionSpecifications.byTargetIdAndIsActiveAndStatus(targetId, Action.Status.CANCELING));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -560,19 +529,17 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
});
|
||||
if (cancelationType == CancelationType.FORCE) {
|
||||
actionRepository.findAll(ActionSpecifications.byDistributionSetIdAndActive(distributionSet.getId()))
|
||||
.forEach(action -> {
|
||||
try {
|
||||
assertTargetUpdateAllowed(action);
|
||||
forceQuitAction0(action.getId());
|
||||
log.debug("Action {} force canceled (force)", action.getId());
|
||||
} catch (final InsufficientPermissionException e) {
|
||||
log.trace("Could not cancel action {} due to insufficient permissions.", action.getId(), e);
|
||||
} catch (final EntityNotFoundException e) {
|
||||
log.trace("Could not cancel action {} due to entity not found exception.", action.getId(),
|
||||
e);
|
||||
}
|
||||
});
|
||||
actionRepository.findAll(ActionSpecifications.byDistributionSetIdAndActive(distributionSet.getId())).forEach(action -> {
|
||||
try {
|
||||
assertTargetUpdateAllowed(action);
|
||||
forceQuitAction0(action.getId());
|
||||
log.debug("Action {} force canceled (force)", action.getId());
|
||||
} catch (final InsufficientPermissionException e) {
|
||||
log.trace("Could not cancel action {} due to insufficient permissions.", action.getId(), e);
|
||||
} catch (final EntityNotFoundException e) {
|
||||
log.trace("Could not cancel action {} due to entity not found exception.", action.getId(), e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -585,9 +552,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
|
||||
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())));
|
||||
return deploymentRequests.stream().collect(Collectors.groupingBy(DeploymentRequest::getDistributionSetId,
|
||||
Collectors.mapping(DeploymentRequest::getTargetWithActionType, Collectors.toList())));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -601,16 +567,13 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
|
||||
private static DistributionSetAssignmentResult buildAssignmentResult(
|
||||
final JpaDistributionSet distributionSet,
|
||||
final List<JpaAction> assignedActions, final int totalTargetsForAssignment) {
|
||||
final JpaDistributionSet distributionSet, final List<JpaAction> assignedActions, final int totalTargetsForAssignment) {
|
||||
final int alreadyAssignedTargetsCount = totalTargetsForAssignment - assignedActions.size();
|
||||
|
||||
return new DistributionSetAssignmentResult(distributionSet, alreadyAssignedTargetsCount, assignedActions);
|
||||
}
|
||||
|
||||
private static String getQueryForDeleteActionsByStatusAndLastModifiedBeforeString(final Database database) {
|
||||
return QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.getOrDefault(database,
|
||||
QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED_DEFAULT);
|
||||
return QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED.getOrDefault(database, QUERY_DELETE_ACTIONS_BY_STATE_AND_LAST_MODIFIED_DEFAULT);
|
||||
}
|
||||
|
||||
private static RetryTemplate createRetryTemplate() {
|
||||
@@ -627,16 +590,13 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
return template;
|
||||
}
|
||||
|
||||
private List<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
final String initiatedBy,
|
||||
final List<DeploymentRequest> deploymentRequests, final String actionMessage,
|
||||
final AbstractDsAssignmentStrategy strategy) {
|
||||
private List<DistributionSetAssignmentResult> assignDistributionSets(final String initiatedBy,
|
||||
final List<DeploymentRequest> deploymentRequests, final String actionMessage, final AbstractDsAssignmentStrategy strategy) {
|
||||
final List<DeploymentRequest> validatedRequests = validateAndFilterRequestForAssignments(deploymentRequests);
|
||||
final Map<Long, List<TargetWithActionType>> assignmentsByDsIds = convertRequest(validatedRequests);
|
||||
|
||||
final List<DistributionSetAssignmentResult> results = assignmentsByDsIds.entrySet().stream()
|
||||
.map(entry -> assignDistributionSetToTargetsWithRetry(
|
||||
initiatedBy, entry.getKey(), entry.getValue(), actionMessage, strategy))
|
||||
.map(entry -> assignDistributionSetToTargetsWithRetry(initiatedBy, entry.getKey(), entry.getValue(), actionMessage, strategy))
|
||||
.toList();
|
||||
strategy.sendDeploymentEvents(results);
|
||||
return results;
|
||||
@@ -660,9 +620,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
private void checkForMultiAssignment(final Collection<DeploymentRequest> deploymentRequests) {
|
||||
if (!isMultiAssignmentsEnabled()) {
|
||||
final long distinctTargetsInRequest = deploymentRequests.stream()
|
||||
.map(request -> request.getTargetWithActionType().getControllerId())
|
||||
.distinct()
|
||||
.count();
|
||||
.map(request -> request.getTargetWithActionType().getControllerId()).distinct().count();
|
||||
if (distinctTargetsInRequest < deploymentRequests.size()) {
|
||||
throw new MultiAssignmentIsNotEnabledException();
|
||||
}
|
||||
@@ -675,13 +633,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
|
||||
private void checkForTargetTypeCompatibility(final List<DeploymentRequest> deploymentRequests) {
|
||||
final List<String> controllerIds = deploymentRequests.stream()
|
||||
.map(DeploymentRequest::getControllerId)
|
||||
.distinct()
|
||||
.toList();
|
||||
final List<Long> distSetIds = deploymentRequests.stream().map(DeploymentRequest::getDistributionSetId)
|
||||
.distinct()
|
||||
.toList();
|
||||
final List<String> controllerIds = deploymentRequests.stream().map(DeploymentRequest::getControllerId).distinct().toList();
|
||||
final List<Long> distSetIds = deploymentRequests.stream().map(DeploymentRequest::getDistributionSetId).distinct().toList();
|
||||
|
||||
if (controllerIds.size() > 1 && distSetIds.size() > 1) {
|
||||
throw new IllegalStateException("Assigning multiple Distribution Sets to multiple Targets simultaneously is not allowed!");
|
||||
@@ -696,14 +649,10 @@ 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<String> incompatibleTargetTypes = ListUtils.partition(controllerIds, Constants.MAX_ENTRIES_IN_STATEMENT)
|
||||
.stream()
|
||||
final Set<String> incompatibleTargetTypes = ListUtils.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::getName)
|
||||
.collect(Collectors.toSet());
|
||||
.and(TargetSpecifications.notCompatibleWithDistributionSetType(distSetType.getId())))).flatMap(List::stream)
|
||||
.map(Target::getTargetType).map(TargetType::getName).collect(Collectors.toSet());
|
||||
|
||||
if (!incompatibleTargetTypes.isEmpty()) {
|
||||
throw new IncompatibleTargetTypeException(incompatibleTargetTypes, distSetType.getName());
|
||||
@@ -717,13 +666,11 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
// we assume that list of assigned DS is less than
|
||||
// MAX_ENTRIES_IN_STATEMENT
|
||||
final Set<DistributionSetType> incompatibleDistSetTypes = distributionSetManagement.get(distSetIds).stream()
|
||||
.map(DistributionSet::getType)
|
||||
.collect(Collectors.toSet());
|
||||
.map(DistributionSet::getType).collect(Collectors.toSet());
|
||||
incompatibleDistSetTypes.removeAll(target.getTargetType().getDistributionSetTypes());
|
||||
|
||||
if (!incompatibleDistSetTypes.isEmpty()) {
|
||||
final Set<String> distSetTypeNames = incompatibleDistSetTypes.stream()
|
||||
.map(DistributionSetType::getName)
|
||||
final Set<String> distSetTypeNames = incompatibleDistSetTypes.stream().map(DistributionSetType::getName)
|
||||
.collect(Collectors.toSet());
|
||||
throw new IncompatibleTargetTypeException(target.getTargetType().getName(), distSetTypeNames);
|
||||
}
|
||||
@@ -731,27 +678,21 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
|
||||
private List<DeploymentRequest> filterByTargetUpdatable(final List<DeploymentRequest> deploymentRequests) {
|
||||
final List<String> controllerIds = deploymentRequests.stream()
|
||||
.map(DeploymentRequest::getControllerId)
|
||||
.distinct()
|
||||
.toList();
|
||||
final List<String> controllerIds = deploymentRequests.stream().map(DeploymentRequest::getControllerId).distinct().toList();
|
||||
|
||||
final List<String> found = targetRepository
|
||||
.findAll(AccessController.Operation.UPDATE, TargetSpecifications.hasControllerIdIn(controllerIds))
|
||||
.stream().map(JpaTarget::getControllerId).toList();
|
||||
final List<String> found = targetRepository.findAll(AccessController.Operation.UPDATE,
|
||||
TargetSpecifications.hasControllerIdIn(controllerIds)).stream().map(JpaTarget::getControllerId).toList();
|
||||
if (found.size() != controllerIds.size()) {
|
||||
return deploymentRequests.stream()
|
||||
.filter(deploymentRequest -> found.contains(deploymentRequest.getControllerId())).toList();
|
||||
return deploymentRequests.stream().filter(deploymentRequest -> found.contains(deploymentRequest.getControllerId())).toList();
|
||||
}
|
||||
return deploymentRequests;
|
||||
}
|
||||
|
||||
private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final String initiatedBy,
|
||||
final Long dsId, final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
|
||||
private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(
|
||||
final String initiatedBy, final Long dsId, final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy) {
|
||||
final RetryCallback<DistributionSetAssignmentResult, ConcurrencyFailureException> retryCallback =
|
||||
retryContext -> assignDistributionSetToTargets(initiatedBy, dsId, targetsWithActionType, actionMessage, assignmentStrategy);
|
||||
return retryTemplate.execute(retryCallback);
|
||||
return retryTemplate.execute(retryContext ->
|
||||
assignDistributionSetToTargets(initiatedBy, dsId, targetsWithActionType, actionMessage, assignmentStrategy));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -777,29 +718,19 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
* {@link DistributionSetType}.
|
||||
*/
|
||||
private DistributionSetAssignmentResult assignDistributionSetToTargets(
|
||||
final String initiatedBy, final Long dsId,
|
||||
final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
|
||||
final String initiatedBy, final Long dsId, final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy) {
|
||||
final JpaDistributionSet distributionSet = distributionSetManagement.getValidAndComplete(dsId);
|
||||
|
||||
JpaDistributionSet distributionSet = distributionSetManagement.getValidAndComplete(dsId);
|
||||
if (distributionSetManagement.isImplicitLockApplicable(distributionSet)) {
|
||||
// without new transaction DS changed event is not thrown
|
||||
DeploymentHelper.runInNewTransaction(txManager, "Implicit lock", status -> {
|
||||
distributionSetManagement.lock(distributionSet.getId());
|
||||
return null;
|
||||
});
|
||||
distributionSet = DeploymentHelper.runInNewTransaction(txManager, "Implicit lock", status -> distributionSetManagement.lock(dsId));
|
||||
}
|
||||
|
||||
final List<String> providedTargetIds = targetsWithActionType.stream()
|
||||
.map(TargetWithActionType::getControllerId)
|
||||
.distinct()
|
||||
.toList();
|
||||
final List<String> providedTargetIds = targetsWithActionType.stream().map(TargetWithActionType::getControllerId).distinct().toList();
|
||||
|
||||
final List<String> existingTargetIds = ListUtils.partition(providedTargetIds, Constants.MAX_ENTRIES_IN_STATEMENT)
|
||||
.stream()
|
||||
final List<String> existingTargetIds = ListUtils.partition(providedTargetIds, Constants.MAX_ENTRIES_IN_STATEMENT).stream()
|
||||
.map(ids -> targetRepository.findAll(AccessController.Operation.UPDATE, TargetSpecifications.hasControllerIdIn(ids)))
|
||||
.flatMap(List::stream).map(JpaTarget::getControllerId)
|
||||
.toList();
|
||||
.flatMap(List::stream).map(JpaTarget::getControllerId).toList();
|
||||
|
||||
final List<JpaTarget> targetEntities = assignmentStrategy.findTargetsForAssignment(existingTargetIds, distributionSet.getId());
|
||||
if (targetEntities.isEmpty()) {
|
||||
@@ -807,21 +738,19 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
|
||||
final List<TargetWithActionType> existingTargetsWithActionType = targetsWithActionType.stream()
|
||||
.filter(target -> existingTargetIds.contains(target.getControllerId()))
|
||||
.toList();
|
||||
.filter(target -> existingTargetIds.contains(target.getControllerId())).toList();
|
||||
|
||||
final List<JpaAction> assignedActions = doAssignDistributionSetToTargets(
|
||||
initiatedBy, existingTargetsWithActionType, actionMessage, assignmentStrategy, distributionSet, targetEntities);
|
||||
return buildAssignmentResult(distributionSet, assignedActions, existingTargetsWithActionType.size());
|
||||
}
|
||||
|
||||
private DistributionSetAssignmentResult allTargetsAlreadyAssignedResult(
|
||||
final JpaDistributionSet distributionSetEntity, final int alreadyAssignedCount) {
|
||||
private DistributionSetAssignmentResult allTargetsAlreadyAssignedResult(final JpaDistributionSet distributionSetEntity,
|
||||
final int alreadyAssignedCount) {
|
||||
// detaching as it is not necessary to persist the set itself
|
||||
entityManager.detach(distributionSetEntity);
|
||||
// return with nothing as all targets had the DS already assigned
|
||||
return new DistributionSetAssignmentResult(distributionSetEntity, alreadyAssignedCount,
|
||||
Collections.emptyList());
|
||||
return new DistributionSetAssignmentResult(distributionSetEntity, alreadyAssignedCount, Collections.emptyList());
|
||||
}
|
||||
|
||||
private List<JpaAction> doAssignDistributionSetToTargets(final String initiatedBy,
|
||||
@@ -837,8 +766,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
// not active before and the manual assignment which has been done cancels them
|
||||
targetEntitiesIdsChunks.forEach(this::cancelInactiveScheduledActionsForTargets);
|
||||
setAssignedDistributionSetAndTargetUpdateStatus(assignmentStrategy, distributionSetEntity, targetEntitiesIdsChunks);
|
||||
final Map<TargetWithActionType, JpaAction> assignedActions = createActions(
|
||||
targetsWithActionType, targetEntities, distributionSetEntity, assignmentStrategy, initiatedBy);
|
||||
final Map<TargetWithActionType, JpaAction> assignedActions = createActions(targetsWithActionType, targetEntities, distributionSetEntity,
|
||||
assignmentStrategy, initiatedBy);
|
||||
// create initial action status when action is created, so we remember
|
||||
// the initial running status because we will change the status
|
||||
// of the action itself and with this action status we have a nicer action history.
|
||||
@@ -856,12 +785,12 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
private void enforceMaxActionsPerTarget(final Collection<DeploymentRequest> deploymentRequests) {
|
||||
final int quota = quotaManagement.getMaxActionsPerTarget();
|
||||
|
||||
final Map<String, Long> countOfTargetInRequest = deploymentRequests.stream()
|
||||
.map(DeploymentRequest::getControllerId)
|
||||
final Map<String, Long> countOfTargetInRequest = deploymentRequests.stream().map(DeploymentRequest::getControllerId)
|
||||
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
|
||||
|
||||
countOfTargetInRequest.forEach((controllerId, count) -> QuotaHelper.assertAssignmentQuota(controllerId, count,
|
||||
quota, Action.class, Target.class, actionRepository::countByTargetControllerId));
|
||||
countOfTargetInRequest.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) {
|
||||
@@ -872,16 +801,15 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
}
|
||||
|
||||
private void setAssignedDistributionSetAndTargetUpdateStatus(
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy,
|
||||
private void setAssignedDistributionSetAndTargetUpdateStatus(final AbstractDsAssignmentStrategy assignmentStrategy,
|
||||
final JpaDistributionSet set, final List<List<Long>> targetIdsChunks) {
|
||||
final String currentUser = auditorProvider.getCurrentAuditor().orElse(null);
|
||||
assignmentStrategy.setAssignedDistributionSetAndTargetStatus(set, targetIdsChunks, currentUser);
|
||||
}
|
||||
|
||||
private Map<TargetWithActionType, JpaAction> createActions(
|
||||
final Collection<TargetWithActionType> targetsWithActionType, final List<JpaTarget> targets, final JpaDistributionSet set,
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy, final String initiatedBy) {
|
||||
private Map<TargetWithActionType, JpaAction> createActions(final Collection<TargetWithActionType> targetsWithActionType,
|
||||
final List<JpaTarget> targets, final JpaDistributionSet set, final AbstractDsAssignmentStrategy assignmentStrategy,
|
||||
final String initiatedBy) {
|
||||
final Map<TargetWithActionType, JpaAction> persistedActions = new LinkedHashMap<>();
|
||||
for (final TargetWithActionType twt : targetsWithActionType) {
|
||||
final JpaAction targetAction = assignmentStrategy.createTargetAction(initiatedBy, twt, targets, set);
|
||||
@@ -892,9 +820,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
return persistedActions;
|
||||
}
|
||||
|
||||
private void createActionsStatus(
|
||||
final Map<TargetWithActionType, JpaAction> actions,
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy, final String actionMessage) {
|
||||
private void createActionsStatus(final Map<TargetWithActionType, JpaAction> actions, final AbstractDsAssignmentStrategy assignmentStrategy,
|
||||
final String actionMessage) {
|
||||
actionStatusRepository.saveAll(actions.entrySet().stream().map(entry -> {
|
||||
final JpaAction action = entry.getValue();
|
||||
final JpaActionStatus actionStatus = assignmentStrategy.createActionStatus(action, actionMessage);
|
||||
@@ -921,8 +848,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
// assignment already
|
||||
if (!isConfirmationRequired) {
|
||||
// confirmation given on assignment dialog
|
||||
actionStatus.addMessage(
|
||||
String.format("Assignment confirmed by initiator [%s].", action.getInitiatedBy()));
|
||||
actionStatus.addMessage(String.format("Assignment confirmed by initiator [%s].", action.getInitiatedBy()));
|
||||
} else if (action.getTarget().getAutoConfirmationStatus() != null) {
|
||||
// auto-confirmation is configured
|
||||
actionStatus.addMessage(action.getTarget().getAutoConfirmationStatus().constructActionMessage());
|
||||
@@ -931,8 +857,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
|
||||
} else {
|
||||
actionStatus
|
||||
.addMessage("Waiting for the confirmation by the device before processing with the deployment");
|
||||
actionStatus.addMessage("Waiting for the confirmation by the device before processing with the deployment");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -952,8 +877,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
|
||||
final JpaTarget target = action.getTarget();
|
||||
if (target.getAssignedDistributionSet() != null
|
||||
&& action.getDistributionSet().getId().equals(target.getAssignedDistributionSet().getId())) {
|
||||
if (target.getAssignedDistributionSet() != null && action.getDistributionSet().getId()
|
||||
.equals(target.getAssignedDistributionSet().getId())) {
|
||||
// the target has already the distribution set assigned, we don't
|
||||
// need to start the scheduled action, just finish it.
|
||||
log.debug("Target {} has distribution set {} assigned. Closing action...", target.getControllerId(),
|
||||
@@ -990,8 +915,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
private List<JpaAction> activateActionsOfRolloutGroup(final List<JpaAction> actions) {
|
||||
actions.forEach(action -> {
|
||||
action.setActive(true);
|
||||
final boolean confirmationRequired = action.getRolloutGroup().isConfirmationRequired()
|
||||
&& action.getTarget().getAutoConfirmationStatus() == null;
|
||||
final boolean confirmationRequired = action.getRolloutGroup().isConfirmationRequired() && action.getTarget()
|
||||
.getAutoConfirmationStatus() == null;
|
||||
if (isConfirmationFlowEnabled() && confirmationRequired) {
|
||||
action.setStatus(Status.WAIT_FOR_CONFIRMATION);
|
||||
return;
|
||||
@@ -1022,18 +947,15 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
|
||||
private boolean isMultiAssignmentsEnabled() {
|
||||
return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement)
|
||||
.isMultiAssignmentsEnabled();
|
||||
return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).isMultiAssignmentsEnabled();
|
||||
}
|
||||
|
||||
private boolean isConfirmationFlowEnabled() {
|
||||
return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement)
|
||||
.isConfirmationFlowEnabled();
|
||||
return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).isConfirmationFlowEnabled();
|
||||
}
|
||||
|
||||
private <T extends Serializable> T getConfigValue(final String key, final Class<T> valueType) {
|
||||
return systemSecurityContext
|
||||
.runAsSystem(() -> tenantConfigurationManagement.getConfigurationValue(key, valueType).getValue());
|
||||
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement.getConfigurationValue(key, valueType).getValue());
|
||||
}
|
||||
|
||||
private void assertTargetReadAllowed(final Long targetId) {
|
||||
@@ -1051,8 +973,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
private JpaAction assertTargetUpdateAllowed(final JpaAction action) {
|
||||
targetRepository.findOne(TargetSpecifications.hasId(action.getTarget().getId())).ifPresentOrElse(
|
||||
target -> targetRepository.getAccessController()
|
||||
.ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target)),
|
||||
() -> {
|
||||
.ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target)), () -> {
|
||||
throw new EntityNotFoundException(Action.class, action);
|
||||
});
|
||||
return action;
|
||||
|
||||
@@ -78,8 +78,7 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
|
||||
public void invalidateDistributionSet(final DistributionSetInvalidation distributionSetInvalidation) {
|
||||
log.debug("Invalidate distribution sets {}", distributionSetInvalidation.getDistributionSetIds());
|
||||
final String tenant = tenantAware.getCurrentTenant();
|
||||
if (shouldRolloutsBeCanceled(distributionSetInvalidation.getCancelationType(),
|
||||
distributionSetInvalidation.isCancelRollouts())) {
|
||||
if (shouldRolloutsBeCanceled(distributionSetInvalidation.getCancelationType(), distributionSetInvalidation.isCancelRollouts())) {
|
||||
final String handlerId = JpaRolloutManagement.createRolloutLockKey(tenant);
|
||||
final Lock lock = lockRegistry.obtain(handlerId);
|
||||
try {
|
||||
@@ -122,17 +121,15 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
|
||||
return cancelationType != CancelationType.NONE || cancelRollouts;
|
||||
}
|
||||
|
||||
private void invalidateDistributionSetsInTransaction(final DistributionSetInvalidation distributionSetInvalidation,
|
||||
final String tenant) {
|
||||
private void invalidateDistributionSetsInTransaction(final DistributionSetInvalidation distributionSetInvalidation, final String tenant) {
|
||||
DeploymentHelper.runInNewTransaction(txManager, tenant + "-invalidateDS", status -> {
|
||||
distributionSetInvalidation.getDistributionSetIds().forEach(setId -> invalidateDistributionSet(setId,
|
||||
distributionSetInvalidation.getCancelationType(), distributionSetInvalidation.isCancelRollouts()));
|
||||
distributionSetInvalidation.getDistributionSetIds().forEach(setId -> invalidateDistributionSet(
|
||||
setId, distributionSetInvalidation.getCancelationType(), distributionSetInvalidation.isCancelRollouts()));
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
private void invalidateDistributionSet(final long setId, final CancelationType cancelationType,
|
||||
final boolean cancelRollouts) {
|
||||
private void invalidateDistributionSet(final long setId, final CancelationType cancelationType, final boolean cancelRollouts) {
|
||||
final DistributionSet distributionSet = distributionSetManagement.getOrElseThrowException(setId);
|
||||
if (!distributionSet.isComplete()) {
|
||||
throw new IncompleteDistributionSetException("Distribution set of type "
|
||||
|
||||
@@ -248,23 +248,27 @@ public class JpaDistributionSetManagement
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
public void lock(final long id) {
|
||||
public JpaDistributionSet lock(final long id) {
|
||||
final JpaDistributionSet distributionSet = getById(id);
|
||||
if (!distributionSet.isLocked()) {
|
||||
if (distributionSet.isLocked()) {
|
||||
return distributionSet;
|
||||
} else {
|
||||
lockSoftwareModules(distributionSet);
|
||||
distributionSet.lock();
|
||||
jpaRepository.save(distributionSet);
|
||||
return jpaRepository.save(distributionSet);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
public void unlock(final long id) {
|
||||
public JpaDistributionSet unlock(final long id) {
|
||||
final JpaDistributionSet distributionSet = getById(id);
|
||||
if (distributionSet.isLocked()) {
|
||||
distributionSet.unlock();
|
||||
jpaRepository.save(distributionSet);
|
||||
return jpaRepository.save(distributionSet);
|
||||
} else {
|
||||
return distributionSet;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
import cz.jirutka.rsql.parser.RSQLParserException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -27,17 +27,12 @@ import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
|
||||
import org.eclipse.hawkbit.repository.builder.GenericTargetFilterQueryUpdate;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryUpdate;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryCreate;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
|
||||
@@ -53,14 +48,11 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.utils.TenantConfigHelper;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -74,9 +66,10 @@ import org.springframework.validation.annotation.Validated;
|
||||
@Validated
|
||||
@Service
|
||||
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "target-filter-management" }, matchIfMissing = true)
|
||||
class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement {
|
||||
class JpaTargetFilterQueryManagement
|
||||
extends AbstractJpaRepositoryManagement<JpaTargetFilterQuery, TargetFilterQueryManagement.Create, TargetFilterQueryManagement.Update, TargetFilterQueryRepository, TargetFilterQueryFields>
|
||||
implements TargetFilterQueryManagement<JpaTargetFilterQuery>{
|
||||
|
||||
private final TargetFilterQueryRepository targetFilterQueryRepository;
|
||||
private final TargetManagement targetManagement;
|
||||
private final DistributionSetManagement<? extends DistributionSet> distributionSetManagement;
|
||||
private final QuotaManagement quotaManagement;
|
||||
@@ -87,12 +80,12 @@ class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement {
|
||||
private final AuditorAware<String> auditorAware;
|
||||
|
||||
protected JpaTargetFilterQueryManagement(
|
||||
final TargetFilterQueryRepository targetFilterQueryRepository,
|
||||
final TargetFilterQueryRepository targetFilterQueryRepository, final EntityManager entityManager,
|
||||
final TargetManagement targetManagement, final DistributionSetManagement<? extends DistributionSet> distributionSetManagement,
|
||||
final QuotaManagement quotaManagement, final TenantConfigurationManagement tenantConfigurationManagement,
|
||||
final RepositoryProperties repositoryProperties,
|
||||
final SystemSecurityContext systemSecurityContext, final ContextAware contextAware, final AuditorAware<String> auditorAware) {
|
||||
this.targetFilterQueryRepository = targetFilterQueryRepository;
|
||||
super(targetFilterQueryRepository, entityManager);
|
||||
this.targetManagement = targetManagement;
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.quotaManagement = quotaManagement;
|
||||
@@ -104,38 +97,22 @@ class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public TargetFilterQuery create(final TargetFilterQueryCreate c) {
|
||||
final JpaTargetFilterQueryCreate create = (JpaTargetFilterQueryCreate) c;
|
||||
public JpaTargetFilterQuery create(final Create create) {
|
||||
validate(create);
|
||||
return super.create(create);
|
||||
}
|
||||
|
||||
create.getQuery().ifPresent(query -> {
|
||||
// validate the RSQL query syntax
|
||||
RsqlUtility.getInstance().validateRsqlFor(query, TargetFields.class, JpaTarget.class);
|
||||
|
||||
// enforce the 'max targets per auto assign' quota right here even
|
||||
// if the result of the filter query can vary over time
|
||||
create.getAutoAssignDistributionSetId().ifPresent(dsId -> {
|
||||
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement)
|
||||
.validate(create);
|
||||
assertMaxTargetsQuota(query, create.getName().orElse(null), dsId);
|
||||
});
|
||||
});
|
||||
|
||||
return targetFilterQueryRepository.save(AccessController.Operation.CREATE, create.build());
|
||||
@Override
|
||||
public List<JpaTargetFilterQuery> create(final Collection<Create> create) {
|
||||
create.forEach(this::validate);
|
||||
return super.create(create);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void delete(final long targetFilterQueryId) {
|
||||
if (!targetFilterQueryRepository.existsById(targetFilterQueryId)) {
|
||||
throw new EntityNotFoundException(TargetFilterQuery.class, targetFilterQueryId);
|
||||
}
|
||||
|
||||
targetFilterQueryRepository.deleteById(targetFilterQueryId);
|
||||
public JpaTargetFilterQuery update(final Update update) {
|
||||
validate(update);
|
||||
return super.update(update);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -149,67 +126,9 @@ class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<TargetFilterQuery> findAll(final Pageable pageable) {
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(targetFilterQueryRepository, null, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return targetFilterQueryRepository.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByAutoAssignDistributionSetId(final long autoAssignDistributionSetId) {
|
||||
return targetFilterQueryRepository.countByAutoAssignDistributionSetId(autoAssignDistributionSetId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<TargetFilterQuery> findByName(final String name, final Pageable pageable) {
|
||||
if (ObjectUtils.isEmpty(name)) {
|
||||
return findAll(pageable);
|
||||
}
|
||||
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(targetFilterQueryRepository,
|
||||
Collections.singletonList(TargetFilterQuerySpecification.likeName(name)), pageable
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByName(final String name) {
|
||||
if (ObjectUtils.isEmpty(name)) {
|
||||
return count();
|
||||
}
|
||||
|
||||
return JpaManagementHelper.countBySpec(targetFilterQueryRepository,
|
||||
Collections.singletonList(TargetFilterQuerySpecification.likeName(name)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<TargetFilterQuery> findByRsql(final String rsqlFilter, final Pageable pageable) {
|
||||
final List<Specification<JpaTargetFilterQuery>> specList = !ObjectUtils.isEmpty(rsqlFilter)
|
||||
? Collections.singletonList(RsqlUtility.getInstance().buildRsqlSpecification(rsqlFilter, TargetFilterQueryFields.class))
|
||||
: Collections.emptyList();
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetFilterQueryRepository, specList, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<TargetFilterQuery> findByQuery(final String query, final Pageable pageable) {
|
||||
final List<Specification<JpaTargetFilterQuery>> specList = !ObjectUtils.isEmpty(query)
|
||||
? Collections.singletonList(TargetFilterQuerySpecification.equalsQuery(query))
|
||||
: Collections.emptyList();
|
||||
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(targetFilterQueryRepository, specList, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<TargetFilterQuery> findByAutoAssignDistributionSetId(final long setId, @NotNull final Pageable pageable) {
|
||||
final DistributionSet distributionSet = distributionSetManagement.getOrElseThrowException(setId);
|
||||
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(targetFilterQueryRepository,
|
||||
Collections.singletonList(TargetFilterQuerySpecification.byAutoAssignDS(distributionSet)), pageable
|
||||
);
|
||||
return jpaRepository.countByAutoAssignDistributionSetId(autoAssignDistributionSetId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -222,59 +141,20 @@ class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement {
|
||||
specList.add(RsqlUtility.getInstance().buildRsqlSpecification(rsql, TargetFilterQueryFields.class));
|
||||
}
|
||||
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetFilterQueryRepository, specList, pageable);
|
||||
return JpaManagementHelper.findAllWithCountBySpec(jpaRepository, specList, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<TargetFilterQuery> findWithAutoAssignDS(final Pageable pageable) {
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(targetFilterQueryRepository,
|
||||
Collections.singletonList(TargetFilterQuerySpecification.withAutoAssignDS()), pageable
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TargetFilterQuery> get(final long targetFilterQueryId) {
|
||||
return targetFilterQueryRepository.findById(targetFilterQueryId).map(TargetFilterQuery.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<TargetFilterQuery> getByName(final String targetFilterQueryName) {
|
||||
return targetFilterQueryRepository.findByName(targetFilterQueryName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public TargetFilterQuery update(final TargetFilterQueryUpdate u) {
|
||||
final GenericTargetFilterQueryUpdate update = (GenericTargetFilterQueryUpdate) u;
|
||||
|
||||
final JpaTargetFilterQuery targetFilterQuery = findTargetFilterQueryOrThrowExceptionIfNotFound(update.getId());
|
||||
|
||||
update.getName().ifPresent(targetFilterQuery::setName);
|
||||
update.getQuery().ifPresent(query -> {
|
||||
|
||||
// enforce the 'max targets per auto assignment'-quota only if the
|
||||
// query is going to change
|
||||
if (targetFilterQuery.getAutoAssignDistributionSet() != null
|
||||
&& !query.equals(targetFilterQuery.getQuery())) {
|
||||
assertMaxTargetsQuota(query, targetFilterQuery.getName(),
|
||||
targetFilterQuery.getAutoAssignDistributionSet().getId());
|
||||
}
|
||||
|
||||
// set the new query
|
||||
targetFilterQuery.setQuery(query);
|
||||
});
|
||||
update.getConfirmationRequired().ifPresent(targetFilterQuery::setConfirmationRequired);
|
||||
|
||||
return targetFilterQueryRepository.save(targetFilterQuery);
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(
|
||||
jpaRepository, List.of(TargetFilterQuerySpecification.withAutoAssignDS()), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public TargetFilterQuery updateAutoAssignDS(final AutoAssignDistributionSetUpdate update) {
|
||||
final JpaTargetFilterQuery targetFilterQuery = findTargetFilterQueryOrThrowExceptionIfNotFound(
|
||||
update.getTargetFilterId());
|
||||
|
||||
if (update.getDsId() == null) {
|
||||
final JpaTargetFilterQuery targetFilterQuery = findTargetFilterQueryOrThrowExceptionIfNotFound(update.targetFilterId());
|
||||
if (update.dsId() == null) {
|
||||
targetFilterQuery.setAccessControlContext(null);
|
||||
targetFilterQuery.setAutoAssignDistributionSet(null);
|
||||
targetFilterQuery.setAutoAssignActionType(null);
|
||||
@@ -283,9 +163,9 @@ class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement {
|
||||
targetFilterQuery.setConfirmationRequired(false);
|
||||
} else {
|
||||
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(update);
|
||||
assertMaxTargetsQuota(targetFilterQuery.getQuery(), targetFilterQuery.getName(), update.getDsId());
|
||||
assertMaxTargetsQuota(targetFilterQuery.getQuery(), targetFilterQuery.getName(), update.dsId());
|
||||
final JpaDistributionSet distributionSet = (JpaDistributionSet) distributionSetManagement
|
||||
.getValidAndComplete(update.getDsId());
|
||||
.getValidAndComplete(update.dsId());
|
||||
|
||||
if (((JpaDistributionSetManagement) distributionSetManagement).isImplicitLockApplicable(distributionSet)) {
|
||||
distributionSetManagement.lock(distributionSet.getId());
|
||||
@@ -293,23 +173,20 @@ class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement {
|
||||
|
||||
targetFilterQuery.setAutoAssignDistributionSet(distributionSet);
|
||||
contextAware.getCurrentContext().ifPresent(targetFilterQuery::setAccessControlContext);
|
||||
targetFilterQuery.setAutoAssignInitiatedBy(
|
||||
auditorAware.getCurrentAuditor().orElse(targetFilterQuery.getCreatedBy()));
|
||||
targetFilterQuery.setAutoAssignActionType(sanitizeAutoAssignActionType(update.getActionType()));
|
||||
targetFilterQuery.setAutoAssignWeight(
|
||||
update.getWeight() == null ? repositoryProperties.getActionWeightIfAbsent() : update.getWeight());
|
||||
targetFilterQuery.setAutoAssignInitiatedBy(auditorAware.getCurrentAuditor().orElse(targetFilterQuery.getCreatedBy()));
|
||||
targetFilterQuery.setAutoAssignActionType(sanitizeAutoAssignActionType(update.actionType()));
|
||||
targetFilterQuery.setAutoAssignWeight(update.weight() == null ? repositoryProperties.getActionWeightIfAbsent() : update.weight());
|
||||
final boolean confirmationRequired =
|
||||
update.getConfirmationRequired() == null ?
|
||||
isConfirmationFlowEnabled() : update.getConfirmationRequired();
|
||||
update.confirmationRequired() == null ? isConfirmationFlowEnabled() : update.confirmationRequired();
|
||||
targetFilterQuery.setConfirmationRequired(confirmationRequired);
|
||||
}
|
||||
return targetFilterQueryRepository.save(targetFilterQuery);
|
||||
return jpaRepository.save(targetFilterQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void cancelAutoAssignmentForDistributionSet(final long distributionSetId) {
|
||||
targetFilterQueryRepository.unsetAutoAssignDistributionSetAndActionTypeAndAccessContext(distributionSetId);
|
||||
jpaRepository.unsetAutoAssignDistributionSetAndActionTypeAndAccessContext(distributionSetId);
|
||||
log.debug("Auto assignments for distribution sets {} deactivated", distributionSetId);
|
||||
}
|
||||
|
||||
@@ -331,8 +208,7 @@ class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement {
|
||||
}
|
||||
|
||||
private JpaTargetFilterQuery findTargetFilterQueryOrThrowExceptionIfNotFound(final Long queryId) {
|
||||
return targetFilterQueryRepository.findById(queryId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, queryId));
|
||||
return jpaRepository.findById(queryId).orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, queryId));
|
||||
}
|
||||
|
||||
private void assertMaxTargetsQuota(final String query, final String filterName, final long dsId) {
|
||||
@@ -340,4 +216,51 @@ class JpaTargetFilterQueryManagement implements TargetFilterQueryManagement {
|
||||
targetManagement.countByRsqlAndNonDSAndCompatibleAndUpdatable(dsId, query),
|
||||
quotaManagement.getMaxTargetsPerAutoAssignment(), Target.class, TargetFilterQuery.class, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void validate(final Create create) {
|
||||
Optional.ofNullable(create.getAutoAssignDistributionSet()).ifPresent(distributionSet -> {
|
||||
if (!distributionSet.isValid()) {
|
||||
throw new InvalidDistributionSetException();
|
||||
}
|
||||
if (!distributionSet.isComplete()) {
|
||||
throw new IncompleteDistributionSetException();
|
||||
}
|
||||
});
|
||||
Optional.ofNullable(create.getAutoAssignActionType()).ifPresent(actionType -> {
|
||||
if (!TargetFilterQuery.ALLOWED_AUTO_ASSIGN_ACTION_TYPES.contains(actionType)) {
|
||||
throw new InvalidAutoAssignActionTypeException();
|
||||
}
|
||||
});
|
||||
Optional.ofNullable(create.getQuery()).ifPresent(query -> {
|
||||
// validate the RSQL query syntax
|
||||
RsqlUtility.getInstance().validateRsqlFor(query, TargetFields.class, JpaTarget.class);
|
||||
|
||||
// enforce the 'max targets per auto assign' quota right here even if the result of the filter query can vary over time
|
||||
Optional.ofNullable(create.getAutoAssignDistributionSet()).ifPresent(dsId -> {
|
||||
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(create);
|
||||
assertMaxTargetsQuota(query, create.getName(), dsId.getId());
|
||||
});
|
||||
});
|
||||
if (create.getAutoAssignWeight() == null) {
|
||||
create.setAutoAssignWeight(create.getAutoAssignDistributionSet() == null ? 0 : repositoryProperties.getActionWeightIfAbsent());
|
||||
}
|
||||
}
|
||||
|
||||
private void validate(final Update update) {
|
||||
final JpaTargetFilterQuery targetFilterQuery = findTargetFilterQueryOrThrowExceptionIfNotFound(update.getId());
|
||||
Optional.ofNullable(update.getQuery()).ifPresent(query -> {
|
||||
// validate the RSQL query syntax
|
||||
RsqlUtility.getInstance().validateRsqlFor(query, TargetFields.class, JpaTarget.class);
|
||||
|
||||
Optional.ofNullable(targetFilterQuery.getAutoAssignDistributionSet()).ifPresent(autoAssignDs -> {
|
||||
// enforce the 'max targets per auto assignment'-quota only if the query is going to change
|
||||
if (!query.equals(targetFilterQuery.getQuery())) {
|
||||
assertMaxTargetsQuota(query, targetFilterQuery.getName(), autoAssignDs.getId());
|
||||
}
|
||||
});
|
||||
|
||||
// set the new query
|
||||
targetFilterQuery.setQuery(query);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -64,11 +64,11 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity imple
|
||||
@NotEmpty
|
||||
private String query;
|
||||
|
||||
@ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = JpaDistributionSet.class)
|
||||
@ManyToOne(fetch = FetchType.LAZY, targetEntity = JpaDistributionSet.class)
|
||||
@JoinColumn(
|
||||
name = "auto_assign_distribution_set",
|
||||
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_target_filter_query_auto_assign_distribution_set"))
|
||||
private JpaDistributionSet autoAssignDistributionSet;
|
||||
private DistributionSet autoAssignDistributionSet;
|
||||
|
||||
@Column(name = "auto_assign_action_type")
|
||||
@Convert(converter = JpaAction.ActionTypeConverter.class)
|
||||
|
||||
@@ -33,22 +33,7 @@ public final class TargetFilterQuerySpecification {
|
||||
* @return the {@link JpaTargetFilterQuery} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaTargetFilterQuery> equalsQuery(final String queryValue) {
|
||||
return (targetFilterQueryRoot, query, cb) -> cb.equal(targetFilterQueryRoot.get(JpaTargetFilterQuery_.query),
|
||||
queryValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link JpaTargetFilterQuery}s based
|
||||
* on is {@link JpaTargetFilterQuery#getName()}.
|
||||
*
|
||||
* @param searchText of the filter
|
||||
* @return the {@link JpaTargetFilterQuery} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaTargetFilterQuery> likeName(final String searchText) {
|
||||
return (targetFilterQueryRoot, query, cb) -> {
|
||||
final String searchTextToLower = searchText.toLowerCase();
|
||||
return cb.like(cb.lower(targetFilterQueryRoot.get(JpaTargetFilterQuery_.name)), searchTextToLower);
|
||||
};
|
||||
return (targetFilterQueryRoot, query, cb) -> cb.equal(targetFilterQueryRoot.get(JpaTargetFilterQuery_.query), queryValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,10 +11,9 @@ package org.eclipse.hawkbit.repository.jpa.utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
|
||||
import org.eclipse.hawkbit.repository.exception.NoWeightProvidedInMultiAssignmentModeException;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryCreate;
|
||||
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
@@ -29,8 +28,8 @@ public final class WeightValidationHelper {
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
private WeightValidationHelper(final SystemSecurityContext systemSecurityContext,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement) {
|
||||
private WeightValidationHelper(
|
||||
final SystemSecurityContext systemSecurityContext, final TenantConfigurationManagement tenantConfigurationManagement) {
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
}
|
||||
@@ -41,8 +40,8 @@ public final class WeightValidationHelper {
|
||||
* @param systemSecurityContext security context used to get the tenant and for execution
|
||||
* @param tenantConfigurationManagement to get the value from
|
||||
*/
|
||||
public static WeightValidationHelper usingContext(final SystemSecurityContext systemSecurityContext,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement) {
|
||||
public static WeightValidationHelper usingContext(
|
||||
final SystemSecurityContext systemSecurityContext, final TenantConfigurationManagement tenantConfigurationManagement) {
|
||||
return new WeightValidationHelper(systemSecurityContext, tenantConfigurationManagement);
|
||||
}
|
||||
|
||||
@@ -74,9 +73,8 @@ public final class WeightValidationHelper {
|
||||
*
|
||||
* @param targetFilterQueryCreate the target filter query
|
||||
*/
|
||||
public void validate(final JpaTargetFilterQueryCreate targetFilterQueryCreate) {
|
||||
validateWeight(targetFilterQueryCreate.getAutoAssignWeight().orElse(null));
|
||||
|
||||
public void validate(final TargetFilterQueryManagement.Create targetFilterQueryCreate) {
|
||||
validateWeight(targetFilterQueryCreate.getAutoAssignWeight());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -84,16 +82,14 @@ public final class WeightValidationHelper {
|
||||
*
|
||||
* @param autoAssignDistributionSetUpdate the auto assignment distribution set update
|
||||
*/
|
||||
public void validate(final AutoAssignDistributionSetUpdate autoAssignDistributionSetUpdate) {
|
||||
validateWeight(autoAssignDistributionSetUpdate.getWeight());
|
||||
|
||||
public void validate(final TargetFilterQueryManagement.AutoAssignDistributionSetUpdate autoAssignDistributionSetUpdate) {
|
||||
validateWeight(autoAssignDistributionSetUpdate.weight());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the weight is valid
|
||||
*
|
||||
* @param weight weight tied to the rollout, auto assignment, or online
|
||||
* assignment.
|
||||
* @param weight weight tied to the rollout, auto assignment, or online assignment.
|
||||
*/
|
||||
public void validateWeight(final Integer weight) {
|
||||
final boolean hasWeight = weight != null;
|
||||
@@ -101,8 +97,7 @@ public final class WeightValidationHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the weight is valid with the multi-assignments being turned
|
||||
* off/on.
|
||||
* Checks if the weight is valid with the multi-assignments being turned off/on.
|
||||
*
|
||||
* @param hasWeight indicator of the weight if it has numerical value
|
||||
* @param hasNoWeight indicator of the weight if it doesn't have a numerical value
|
||||
@@ -119,4 +114,4 @@ public final class WeightValidationHelper {
|
||||
throw new NoWeightProvidedInMultiAssignmentModeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user