[#1651] Implement skip DistributionSet implicit lock on DS tags (#1680)

tags the implicit lock is skipped on are configured via
RepositoryProperties.skipImplicitLockForTags list.
By default skip tags are the ones with names:
"skip-implicit-lock", "skip_implicit_lock", "SKIP_IMPLICIT_LOCK", "SKIP-IMPLICIT-LOCK"

+ this commit centralize the implicit lock enable/disable logic

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2024-03-08 14:58:31 +02:00
committed by GitHub
parent 3f060e84a3
commit 936e6d6d66
10 changed files with 110 additions and 82 deletions

View File

@@ -505,7 +505,7 @@ public interface DistributionSetManagement
* if set or tag with given ID does not exist * if set or tag with given ID does not exist
*/ */
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
DistributionSet unAssignTag(long id, long tagId); DistributionSet unassignTag(long id, long tagId);
/** /**
* Updates a distribution set meta data value if corresponding entry exists. * Updates a distribution set meta data value if corresponding entry exists.

View File

@@ -9,6 +9,7 @@
*/ */
package org.eclipse.hawkbit.repository; package org.eclipse.hawkbit.repository;
import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import lombok.Data; import lombok.Data;
@@ -68,4 +69,7 @@ public class RepositoryProperties {
private long dsInvalidationLockTimeout = 5; private long dsInvalidationLockTimeout = 5;
private boolean implicitTenantCreateAllowed; private boolean implicitTenantCreateAllowed;
private List<String> skipImplicitLockForTags =
List.of("skip-implicit-lock", "skip_implicit_lock", "SKIP_IMPLICIT_LOCK", "SKIP-IMPLICIT-LOCK");
} }

View File

@@ -164,6 +164,7 @@ import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver; import org.eclipse.hawkbit.tenancy.UserAuthoritiesResolver;
import org.eclipse.hawkbit.utils.TenantConfigHelper;
import org.eclipse.persistence.config.PersistenceUnitProperties; import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.hibernate.validator.BaseHibernateValidatorConfiguration; import org.hibernate.validator.BaseHibernateValidatorConfiguration;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
@@ -532,25 +533,24 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
DistributionSetManagement distributionSetManagement(final EntityManager entityManager, DistributionSetManagement distributionSetManagement(
final EntityManager entityManager,
final DistributionSetRepository distributionSetRepository, final DistributionSetRepository distributionSetRepository,
final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement, final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement,
final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement, final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement,
final DistributionSetMetadataRepository distributionSetMetadataRepository, final DistributionSetMetadataRepository distributionSetMetadataRepository,
final TargetRepository targetRepository, final TargetRepository targetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository, final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository,
final EventPublisherHolder eventPublisherHolder, final TenantAware tenantAware, final SystemSecurityContext systemSecurityContext, final TenantConfigurationManagement tenantConfigurationManagement,
final VirtualPropertyReplacer virtualPropertyReplacer, final VirtualPropertyReplacer virtualPropertyReplacer, final SoftwareModuleRepository softwareModuleRepository,
final SoftwareModuleRepository softwareModuleRepository,
final DistributionSetTagRepository distributionSetTagRepository, final DistributionSetTagRepository distributionSetTagRepository,
final AfterTransactionCommitExecutor afterCommit, final JpaProperties properties, final RepositoryProperties repositoryProperties) {
final JpaProperties properties) {
return new JpaDistributionSetManagement(entityManager, distributionSetRepository, distributionSetTagManagement, return new JpaDistributionSetManagement(entityManager, distributionSetRepository, distributionSetTagManagement,
systemManagement, distributionSetTypeManagement, quotaManagement, distributionSetMetadataRepository, systemManagement, distributionSetTypeManagement, quotaManagement, distributionSetMetadataRepository,
targetRepository, targetFilterQueryRepository, actionRepository, eventPublisherHolder, tenantAware, targetRepository, targetFilterQueryRepository, actionRepository,
virtualPropertyReplacer, softwareModuleRepository, distributionSetTagRepository, afterCommit, TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement),
properties.getDatabase()); virtualPropertyReplacer, softwareModuleRepository, distributionSetTagRepository,
properties.getDatabase(), repositoryProperties);
} }
/** /**

View File

@@ -9,7 +9,6 @@
*/ */
package org.eclipse.hawkbit.repository.jpa.management; package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.IMPLICIT_LOCK_ENABLED;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED; import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED;
import java.io.Serializable; import java.io.Serializable;
@@ -373,8 +372,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
final JpaDistributionSet distributionSet = final JpaDistributionSet distributionSet =
(JpaDistributionSet) distributionSetManagement.getValidAndComplete(dsId); (JpaDistributionSet) distributionSetManagement.getValidAndComplete(dsId);
// implicit lock if (((JpaDistributionSetManagement)distributionSetManagement).isImplicitLockApplicable(distributionSet)) {
if (!distributionSet.isLocked() && getConfigValue(IMPLICIT_LOCK_ENABLED, Boolean.class)) {
// without new transaction DS changed event is not thrown // without new transaction DS changed event is not thrown
DeploymentHelper.runInNewTransaction(txManager, "Implicit lock", status -> { DeploymentHelper.runInNewTransaction(txManager, "Implicit lock", status -> {
distributionSetManagement.lock(distributionSet.getId()); distributionSetManagement.lock(distributionSet.getId());

View File

@@ -15,6 +15,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -29,11 +30,11 @@ import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement; import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.QuotaManagement; import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.repository.builder.DistributionSetCreate; import org.eclipse.hawkbit.repository.builder.DistributionSetCreate;
import org.eclipse.hawkbit.repository.builder.DistributionSetUpdate; import org.eclipse.hawkbit.repository.builder.DistributionSetUpdate;
import org.eclipse.hawkbit.repository.builder.GenericDistributionSetUpdate; import org.eclipse.hawkbit.repository.builder.GenericDistributionSetUpdate;
import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent;
import org.eclipse.hawkbit.repository.exception.DeletedException; import org.eclipse.hawkbit.repository.exception.DeletedException;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
@@ -45,7 +46,6 @@ import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController; import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetCreate; import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetCreate;
import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
import org.eclipse.hawkbit.repository.jpa.model.DsMetadataCompositeKey; import org.eclipse.hawkbit.repository.jpa.model.DsMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
@@ -62,6 +62,7 @@ import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetSpecification; import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetSpecification;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications; import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -73,9 +74,8 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.MetaData; import org.eclipse.hawkbit.repository.model.MetaData;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Statistic; import org.eclipse.hawkbit.repository.model.Statistic;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.utils.TenantConfigHelper;
import org.springframework.dao.ConcurrencyFailureException; import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@@ -85,11 +85,14 @@ import org.springframework.data.jpa.domain.Specification;
import org.springframework.orm.jpa.vendor.Database; import org.springframework.orm.jpa.vendor.Database;
import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable; import org.springframework.retry.annotation.Retryable;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.IMPLICIT_LOCK_ENABLED;
/** /**
* JPA implementation of {@link DistributionSetManagement}. * JPA implementation of {@link DistributionSetManagement}.
* *
@@ -99,51 +102,36 @@ import org.springframework.validation.annotation.Validated;
public class JpaDistributionSetManagement implements DistributionSetManagement { public class JpaDistributionSetManagement implements DistributionSetManagement {
private final EntityManager entityManager; private final EntityManager entityManager;
private final DistributionSetRepository distributionSetRepository; private final DistributionSetRepository distributionSetRepository;
private final DistributionSetTagManagement distributionSetTagManagement; private final DistributionSetTagManagement distributionSetTagManagement;
private final SystemManagement systemManagement; private final SystemManagement systemManagement;
private final DistributionSetTypeManagement distributionSetTypeManagement; private final DistributionSetTypeManagement distributionSetTypeManagement;
private final QuotaManagement quotaManagement; private final QuotaManagement quotaManagement;
private final DistributionSetMetadataRepository distributionSetMetadataRepository; private final DistributionSetMetadataRepository distributionSetMetadataRepository;
private final TargetRepository targetRepository; private final TargetRepository targetRepository;
private final TargetFilterQueryRepository targetFilterQueryRepository; private final TargetFilterQueryRepository targetFilterQueryRepository;
private final ActionRepository actionRepository; private final ActionRepository actionRepository;
private final TenantConfigHelper tenantConfigHelper;
private final EventPublisherHolder eventPublisherHolder;
private final TenantAware tenantAware;
private final VirtualPropertyReplacer virtualPropertyReplacer; private final VirtualPropertyReplacer virtualPropertyReplacer;
private final SoftwareModuleRepository softwareModuleRepository; private final SoftwareModuleRepository softwareModuleRepository;
private final DistributionSetTagRepository distributionSetTagRepository; private final DistributionSetTagRepository distributionSetTagRepository;
private final AfterTransactionCommitExecutor afterCommit;
private final Database database; private final Database database;
private final RepositoryProperties repositoryProperties;
public JpaDistributionSetManagement(final EntityManager entityManager, public JpaDistributionSetManagement(
final EntityManager entityManager,
final DistributionSetRepository distributionSetRepository, final DistributionSetRepository distributionSetRepository,
final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement, final DistributionSetTagManagement distributionSetTagManagement, final SystemManagement systemManagement,
final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement, final DistributionSetTypeManagement distributionSetTypeManagement, final QuotaManagement quotaManagement,
final DistributionSetMetadataRepository distributionSetMetadataRepository, final DistributionSetMetadataRepository distributionSetMetadataRepository,
final TargetRepository targetRepository, final TargetRepository targetRepository,
final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository, final TargetFilterQueryRepository targetFilterQueryRepository, final ActionRepository actionRepository,
final EventPublisherHolder eventPublisherHolder, final TenantAware tenantAware, final TenantConfigHelper tenantConfigHelper,
final VirtualPropertyReplacer virtualPropertyReplacer, final VirtualPropertyReplacer virtualPropertyReplacer,
final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleRepository softwareModuleRepository,
final DistributionSetTagRepository distributionSetTagRepository, final DistributionSetTagRepository distributionSetTagRepository,
final AfterTransactionCommitExecutor afterCommit, final Database database,
final Database database) { final RepositoryProperties repositoryProperties) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.distributionSetRepository = distributionSetRepository; this.distributionSetRepository = distributionSetRepository;
this.distributionSetTagManagement = distributionSetTagManagement; this.distributionSetTagManagement = distributionSetTagManagement;
@@ -154,13 +142,12 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
this.targetRepository = targetRepository; this.targetRepository = targetRepository;
this.targetFilterQueryRepository = targetFilterQueryRepository; this.targetFilterQueryRepository = targetFilterQueryRepository;
this.actionRepository = actionRepository; this.actionRepository = actionRepository;
this.eventPublisherHolder = eventPublisherHolder; this.tenantConfigHelper = tenantConfigHelper;
this.tenantAware = tenantAware;
this.virtualPropertyReplacer = virtualPropertyReplacer; this.virtualPropertyReplacer = virtualPropertyReplacer;
this.softwareModuleRepository = softwareModuleRepository; this.softwareModuleRepository = softwareModuleRepository;
this.distributionSetTagRepository = distributionSetTagRepository; this.distributionSetTagRepository = distributionSetTagRepository;
this.afterCommit = afterCommit;
this.database = database; this.database = database;
this.repositoryProperties = repositoryProperties;
} }
@Override @Override
@@ -679,7 +666,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Transactional @Transactional
@Retryable(include = { @Retryable(include = {
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public DistributionSet unAssignTag(final long id, final long dsTagId) { public DistributionSet unassignTag(final long id, final long dsTagId) {
final JpaDistributionSet set = (JpaDistributionSet) getWithDetails(id) final JpaDistributionSet set = (JpaDistributionSet) getWithDetails(id)
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, id)); .orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, id));
@@ -830,6 +817,36 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
distributionSetRepository.save(jpaSet); distributionSetRepository.save(jpaSet);
} }
// check if shall implicitly lock a distribution set
boolean isImplicitLockApplicable(final DistributionSet distributionSet) {
final JpaDistributionSet jpaDistributionSet = (JpaDistributionSet) distributionSet;
if (jpaDistributionSet.isLocked()) {
// already locked
return false;
}
if (!tenantConfigHelper.getConfigValue(IMPLICIT_LOCK_ENABLED, Boolean.class)) {
// implicit lock disabled
return false;
}
final List<String> skipForTags = repositoryProperties.getSkipImplicitLockForTags();
if (!ObjectUtils.isEmpty(skipForTags)) {
final Set<DistributionSetTag> tags = ((JpaDistributionSet)jpaDistributionSet).getTags();
if (!ObjectUtils.isEmpty(tags)) {
for (final DistributionSetTag tag : tags) {
if (skipForTags.contains(tag.getName())) {
// has a skip tag
return false;
}
}
}
}
// finally - implicit lock is applicable
return true;
}
private JpaDistributionSet getById(final long id) { private JpaDistributionSet getById(final long id) {
return distributionSetRepository return distributionSetRepository
.findById(id) .findById(id)

View File

@@ -10,7 +10,6 @@
package org.eclipse.hawkbit.repository.jpa.management; package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate.addSuccessAndErrorConditionsAndActions; import static org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate.addSuccessAndErrorConditionsAndActions;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.IMPLICIT_LOCK_ENABLED;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -59,6 +58,7 @@ import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRolloutGroupSuccessAction; import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRolloutGroupSuccessAction;
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility; import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification; import org.eclipse.hawkbit.repository.jpa.specifications.RolloutSpecification;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.jpa.utils.WeightValidationHelper; import org.eclipse.hawkbit.repository.jpa.utils.WeightValidationHelper;
import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -75,7 +75,6 @@ import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder; import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.utils.TenantConfigHelper;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.ConcurrencyFailureException; import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@@ -144,8 +143,6 @@ public class JpaRolloutManagement implements RolloutManagement {
private final ContextAware contextAware; private final ContextAware contextAware;
private final Database database; private final Database database;
private final TenantConfigHelper tenantConfigHelper;
public JpaRolloutManagement(final TargetManagement targetManagement, public JpaRolloutManagement(final TargetManagement targetManagement,
final DistributionSetManagement distributionSetManagement, final EventPublisherHolder eventPublisherHolder, final DistributionSetManagement distributionSetManagement, final EventPublisherHolder eventPublisherHolder,
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database, final VirtualPropertyReplacer virtualPropertyReplacer, final Database database,
@@ -162,8 +159,6 @@ public class JpaRolloutManagement implements RolloutManagement {
this.systemSecurityContext = systemSecurityContext; this.systemSecurityContext = systemSecurityContext;
this.eventPublisherHolder = eventPublisherHolder; this.eventPublisherHolder = eventPublisherHolder;
this.contextAware = contextAware; this.contextAware = contextAware;
tenantConfigHelper = TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement);
} }
@Override @Override
@@ -229,8 +224,7 @@ public class JpaRolloutManagement implements RolloutManagement {
} }
rollout.setTotalTargets(totalTargets); rollout.setTotalTargets(totalTargets);
// implicit lock if (((JpaDistributionSetManagement)distributionSetManagement).isImplicitLockApplicable(distributionSet)) {
if (!distributionSet.isLocked() && tenantConfigHelper.getConfigValue(IMPLICIT_LOCK_ENABLED, Boolean.class)) {
distributionSetManagement.lock(distributionSet.getId()); distributionSetManagement.lock(distributionSet.getId());
} }

View File

@@ -9,8 +9,6 @@
*/ */
package org.eclipse.hawkbit.repository.jpa.management; package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.IMPLICIT_LOCK_ENABLED;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -77,20 +75,15 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
private final TargetFilterQueryRepository targetFilterQueryRepository; private final TargetFilterQueryRepository targetFilterQueryRepository;
private final TargetManagement targetManagement; private final TargetManagement targetManagement;
private final VirtualPropertyReplacer virtualPropertyReplacer; private final VirtualPropertyReplacer virtualPropertyReplacer;
private final DistributionSetManagement distributionSetManagement; private final DistributionSetManagement distributionSetManagement;
private final QuotaManagement quotaManagement; private final QuotaManagement quotaManagement;
private final TenantConfigurationManagement tenantConfigurationManagement; private final TenantConfigurationManagement tenantConfigurationManagement;
private final RepositoryProperties repositoryProperties; private final RepositoryProperties repositoryProperties;
private final SystemSecurityContext systemSecurityContext; private final SystemSecurityContext systemSecurityContext;
private final ContextAware contextAware; private final ContextAware contextAware;
private final Database database; private final Database database;
private final TenantConfigHelper tenantConfigHelper;
public JpaTargetFilterQueryManagement(final TargetFilterQueryRepository targetFilterQueryRepository, public JpaTargetFilterQueryManagement(final TargetFilterQueryRepository targetFilterQueryRepository,
final TargetManagement targetManagement, final VirtualPropertyReplacer virtualPropertyReplacer, final TargetManagement targetManagement, final VirtualPropertyReplacer virtualPropertyReplacer,
final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement, final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
@@ -107,8 +100,6 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
this.repositoryProperties = repositoryProperties; this.repositoryProperties = repositoryProperties;
this.systemSecurityContext = systemSecurityContext; this.systemSecurityContext = systemSecurityContext;
this.contextAware = contextAware; this.contextAware = contextAware;
tenantConfigHelper = TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement);
} }
@Override @Override
@@ -285,8 +276,7 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
final JpaDistributionSet distributionSet = (JpaDistributionSet) distributionSetManagement final JpaDistributionSet distributionSet = (JpaDistributionSet) distributionSetManagement
.getValidAndComplete(update.getDsId()); .getValidAndComplete(update.getDsId());
// implicit lock if (((JpaDistributionSetManagement)distributionSetManagement).isImplicitLockApplicable(distributionSet)) {
if (!distributionSet.isLocked() && tenantConfigHelper.getConfigValue(IMPLICIT_LOCK_ENABLED, Boolean.class)) {
distributionSetManagement.lock(distributionSet.getId()); distributionSetManagement.lock(distributionSet.getId());
} }

View File

@@ -31,7 +31,6 @@ import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetFilter; import org.eclipse.hawkbit.repository.model.DistributionSetFilter;
import org.eclipse.hawkbit.repository.model.DistributionSetTag; import org.eclipse.hawkbit.repository.model.DistributionSetTag;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery; import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@@ -219,7 +218,7 @@ class DistributionSetAccessControllerTest extends AbstractAccessControllerTest {
assertThat(distributionSetManagement.assignTag(Collections.singletonList(permitted.getId()), dsTag.getId())) assertThat(distributionSetManagement.assignTag(Collections.singletonList(permitted.getId()), dsTag.getId()))
.hasSize(1); .hasSize(1);
// verify distributionSetManagement#unAssignTag on permitted target // verify distributionSetManagement#unAssignTag on permitted target
assertThat(distributionSetManagement.unAssignTag(permitted.getId(), dsTag.getId()).getId()) assertThat(distributionSetManagement.unassignTag(permitted.getId(), dsTag.getId()).getId())
.isEqualTo(permitted.getId()); .isEqualTo(permitted.getId());
// assignment is denied for readOnlyTarget (read, but no update permissions) // assignment is denied for readOnlyTarget (read, but no update permissions)
@@ -237,7 +236,7 @@ class DistributionSetAccessControllerTest extends AbstractAccessControllerTest {
// assignment is denied for readOnlyTarget (read, but no update permissions) // assignment is denied for readOnlyTarget (read, but no update permissions)
assertThatThrownBy(() -> { assertThatThrownBy(() -> {
distributionSetManagement.unAssignTag(readOnly.getId(), dsTag.getId()); distributionSetManagement.unassignTag(readOnly.getId(), dsTag.getId());
}).as("Missing update permissions for target to toggle tag assignment.") }).as("Missing update permissions for target to toggle tag assignment.")
.isInstanceOf(InsufficientPermissionException.class); .isInstanceOf(InsufficientPermissionException.class);
@@ -256,7 +255,7 @@ class DistributionSetAccessControllerTest extends AbstractAccessControllerTest {
// assignment is denied for hiddenTarget since it's hidden // assignment is denied for hiddenTarget since it's hidden
assertThatThrownBy(() -> { assertThatThrownBy(() -> {
distributionSetManagement.unAssignTag(hidden.getId(), dsTag.getId()); distributionSetManagement.unassignTag(hidden.getId(), dsTag.getId());
}).as("Missing update permissions for target to toggle tag assignment.") }).as("Missing update permissions for target to toggle tag assignment.")
.isInstanceOf(EntityNotFoundException.class); .isInstanceOf(EntityNotFoundException.class);
} }

View File

@@ -31,6 +31,7 @@ import jakarta.validation.ConstraintViolationException;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.assertj.core.api.Condition; import org.assertj.core.api.Condition;
import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.builder.DistributionSetCreate; import org.eclipse.hawkbit.repository.builder.DistributionSetCreate;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagCreatedEvent;
@@ -67,6 +68,7 @@ import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents; import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.repository.test.util.WithUser; import org.eclipse.hawkbit.repository.test.util.WithUser;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
@@ -142,10 +144,10 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
() -> distributionSetManagement.toggleTagAssignment(singletonList(set.getId()), NOT_EXIST_ID), () -> distributionSetManagement.toggleTagAssignment(singletonList(set.getId()), NOT_EXIST_ID),
"DistributionSetTag"); "DistributionSetTag");
verifyThrownExceptionBy(() -> distributionSetManagement.unAssignTag(set.getId(), NOT_EXIST_IDL), verifyThrownExceptionBy(() -> distributionSetManagement.unassignTag(set.getId(), NOT_EXIST_IDL),
"DistributionSetTag"); "DistributionSetTag");
verifyThrownExceptionBy(() -> distributionSetManagement.unAssignTag(NOT_EXIST_IDL, dsTag.getId()), verifyThrownExceptionBy(() -> distributionSetManagement.unassignTag(NOT_EXIST_IDL, dsTag.getId()),
"DistributionSet"); "DistributionSet");
verifyThrownExceptionBy( verifyThrownExceptionBy(
@@ -424,7 +426,7 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
.isEqualTo(distributionSetManagement.findByTag(PAGE, tag.getId()).getNumberOfElements()); .isEqualTo(distributionSetManagement.findByTag(PAGE, tag.getId()).getNumberOfElements());
final JpaDistributionSet unAssignDS = (JpaDistributionSet) distributionSetManagement final JpaDistributionSet unAssignDS = (JpaDistributionSet) distributionSetManagement
.unAssignTag(assignDS.get(0), findDistributionSetTag.getId()); .unassignTag(assignDS.get(0), findDistributionSetTag.getId());
assertThat(unAssignDS.getId()).as("unassigned ds is wrong").isEqualTo(assignDS.get(0)); assertThat(unAssignDS.getId()).as("unassigned ds is wrong").isEqualTo(assignDS.get(0));
assertThat(unAssignDS.getTags().size()).as("unassigned ds has wrong tag size").isZero(); assertThat(unAssignDS.getTags().size()).as("unassigned ds has wrong tag size").isZero();
assertThat(distributionSetTagManagement.getByName(TAG1_NAME)).isPresent(); assertThat(distributionSetTagManagement.getByName(TAG1_NAME)).isPresent();
@@ -1042,11 +1044,9 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
assertThat(softwareModuleCount).isNotEqualTo(0); assertThat(softwareModuleCount).isNotEqualTo(0);
distributionSetManagement.lock(distributionSet.getId()); distributionSetManagement.lock(distributionSet.getId());
assertThat( assertThat(
distributionSetManagement.get(distributionSet.getId()).map(DistributionSet::isLocked) distributionSetManagement.get(distributionSet.getId()).map(DistributionSet::isLocked).orElse(false))
.orElse(false))
.isTrue(); .isTrue();
// try add // try add
assertThatExceptionOfType(LockedException.class) assertThatExceptionOfType(LockedException.class)
.as("Attempt to modify a locked DS software modules should throw an exception") .as("Attempt to modify a locked DS software modules should throw an exception")
@@ -1066,6 +1066,32 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
.isEqualTo(softwareModuleCount); .isEqualTo(softwareModuleCount);
} }
@Autowired RepositoryProperties repositoryProperties;
@Test
@Description("Test implicit locks for a DS and skip tags.")
void isImplicitLockApplicableDistributionSet() {
final JpaDistributionSetManagement distributionSetManagement =
(JpaDistributionSetManagement)this.distributionSetManagement;
final DistributionSet distributionSet = testdataFactory.createDistributionSet("ds-non-skip");
// assert that implicit lock is applicable for non skip tags
assertThat(distributionSetManagement.isImplicitLockApplicable(distributionSet)).isTrue();
assertThat(repositoryProperties.getSkipImplicitLockForTags().size()).isNotEqualTo(0);
final List<DistributionSetTag> skipTags = distributionSetTagManagement.create(
repositoryProperties.getSkipImplicitLockForTags().stream()
.map(skipTag -> entityFactory.tag().create().name(skipTag))
.toList());
// assert that implicit lock locks for every skip tag
skipTags.forEach(skipTag -> {
DistributionSet distributionSetWithSkipTag =
testdataFactory.createDistributionSet("ds-skip-" + skipTag.getName());
distributionSetManagement.assignTag(List.of(distributionSetWithSkipTag.getId()), skipTag.getId());
distributionSetWithSkipTag = distributionSetManagement.get(distributionSetWithSkipTag.getId()).orElseThrow();
// assert that implicit lock isn't applicable for skip tags
assertThat(distributionSetManagement.isImplicitLockApplicable(distributionSetWithSkipTag)).isFalse();
});
}
@Test @Test
@Description("Locks an incomplete DS. Expected behaviour is to throw an exception and to do not lock it.") @Description("Locks an incomplete DS. Expected behaviour is to throw an exception and to do not lock it.")
void lockIncompleteDistributionSetFails() { void lockIncompleteDistributionSetFails() {

View File

@@ -202,7 +202,7 @@ public class MgmtDistributionSetTagResource implements MgmtDistributionSetTagRes
@PathVariable("distributionsetTagId") final Long distributionsetTagId, @PathVariable("distributionsetTagId") final Long distributionsetTagId,
@PathVariable("distributionsetId") final Long distributionsetId) { @PathVariable("distributionsetId") final Long distributionsetId) {
log.debug("Unassign ds {} for ds tag {}", distributionsetId, distributionsetTagId); log.debug("Unassign ds {} for ds tag {}", distributionsetId, distributionsetTagId);
this.distributionSetManagement.unAssignTag(distributionsetId, distributionsetTagId); this.distributionSetManagement.unassignTag(distributionsetId, distributionsetTagId);
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} }