Improve artifact binary cleanup - only after commit (#2134)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2024-12-10 13:41:35 +02:00
committed by GitHub
parent d0c952f236
commit d3eeb71826
8 changed files with 128 additions and 120 deletions

View File

@@ -476,9 +476,8 @@ public class JpaRolloutExecutor implements RolloutExecutor {
} }
private void updateTotalTargetCount(final JpaRolloutGroup rolloutGroup, final long countTargetsOfRolloutGroup) { private void updateTotalTargetCount(final JpaRolloutGroup rolloutGroup, final long countTargetsOfRolloutGroup) {
final JpaRollout jpaRollout = (JpaRollout) rolloutGroup.getRollout(); final JpaRollout jpaRollout = rolloutGroup.getRollout();
final long updatedTargetCount = jpaRollout.getTotalTargets() final long updatedTargetCount = jpaRollout.getTotalTargets() - (rolloutGroup.getTotalTargets() - countTargetsOfRolloutGroup);
- (rolloutGroup.getTotalTargets() - countTargetsOfRolloutGroup);
jpaRollout.setTotalTargets(updatedTargetCount); jpaRollout.setTotalTargets(updatedTargetCount);
rolloutGroup.setTotalTargets((int) countTargetsOfRolloutGroup); rolloutGroup.setTotalTargets((int) countTargetsOfRolloutGroup);
rolloutRepository.save(jpaRollout); rolloutRepository.save(jpaRollout);
@@ -574,12 +573,8 @@ public class JpaRolloutExecutor implements RolloutExecutor {
private boolean ensureAllGroupsAreScheduled(final Rollout rollout) { private boolean ensureAllGroupsAreScheduled(final Rollout rollout) {
final JpaRollout jpaRollout = (JpaRollout) rollout; final JpaRollout jpaRollout = (JpaRollout) rollout;
final List<JpaRolloutGroup> groupsToBeScheduled = rolloutGroupRepository.findByRolloutAndStatus(rollout, RolloutGroupStatus.READY);
final List<JpaRolloutGroup> groupsToBeScheduled = rolloutGroupRepository.findByRolloutAndStatus(rollout, final long scheduledGroups = groupsToBeScheduled.stream().filter(group -> scheduleRolloutGroup(jpaRollout, group)).count();
RolloutGroupStatus.READY);
final long scheduledGroups = groupsToBeScheduled.stream()
.filter(group -> scheduleRolloutGroup(jpaRollout, group)).count();
return scheduledGroups == groupsToBeScheduled.size(); return scheduledGroups == groupsToBeScheduled.size();
} }
@@ -630,8 +625,8 @@ public class JpaRolloutExecutor implements RolloutExecutor {
do { do {
// Add up to TRANSACTION_TARGETS of the left targets // Add up to TRANSACTION_TARGETS of the left targets
// In case a TransactionException is thrown this loop aborts // In case a TransactionException is thrown this loop aborts
final long assigned = assignTargetsToGroupInNewTransaction(rollout, group, groupTargetFilter, final long assigned = assignTargetsToGroupInNewTransaction(
Math.min(TRANSACTION_TARGETS, targetsLeftToAdd)); rollout, group, groupTargetFilter, Math.min(TRANSACTION_TARGETS, targetsLeftToAdd));
if (assigned == 0) { if (assigned == 0) {
break; // percent > 100 or some could have disappeared break; // percent > 100 or some could have disappeared
} else { } else {

View File

@@ -357,8 +357,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final List<RolloutGroupConditionEvaluator<RolloutGroup.RolloutGroupSuccessCondition>> successConditionEvaluators, final List<RolloutGroupConditionEvaluator<RolloutGroup.RolloutGroupSuccessCondition>> successConditionEvaluators,
final List<RolloutGroupActionEvaluator<RolloutGroup.RolloutGroupErrorAction>> errorActionEvaluators, final List<RolloutGroupActionEvaluator<RolloutGroup.RolloutGroupErrorAction>> errorActionEvaluators,
final List<RolloutGroupActionEvaluator<RolloutGroup.RolloutGroupSuccessAction>> successActionEvaluators) { final List<RolloutGroupActionEvaluator<RolloutGroup.RolloutGroupSuccessAction>> successActionEvaluators) {
return new RolloutGroupEvaluationManager(errorConditionEvaluators, successConditionEvaluators, return new RolloutGroupEvaluationManager(
errorActionEvaluators, successActionEvaluators); errorConditionEvaluators, successConditionEvaluators, errorActionEvaluators, successActionEvaluators);
} }
@Bean @Bean
@@ -696,8 +696,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final DistributionSetTagRepository distributionSetTagRepository, final DistributionSetTagRepository distributionSetTagRepository,
final DistributionSetRepository distributionSetRepository, final DistributionSetRepository distributionSetRepository,
final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties) { final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties) {
return new JpaDistributionSetTagManagement(distributionSetTagRepository, distributionSetRepository, return new JpaDistributionSetTagManagement(
virtualPropertyReplacer, properties.getDatabase()); distributionSetTagRepository, distributionSetRepository, virtualPropertyReplacer, properties.getDatabase());
} }
/** /**
@@ -735,8 +735,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final SoftwareModuleRepository softwareModuleRepository, final SoftwareModuleRepository softwareModuleRepository,
final JpaProperties properties) { final JpaProperties properties) {
return new JpaSoftwareModuleTypeManagement(distributionSetTypeRepository, softwareModuleTypeRepository, return new JpaSoftwareModuleTypeManagement(distributionSetTypeRepository, softwareModuleTypeRepository,
virtualPropertyReplacer, softwareModuleRepository, virtualPropertyReplacer, softwareModuleRepository, properties.getDatabase());
properties.getDatabase());
} }
@Bean @Bean
@@ -744,8 +743,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
RolloutHandler rolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement, RolloutHandler rolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement,
final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry, final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry,
final PlatformTransactionManager txManager, final ContextAware contextAware) { final PlatformTransactionManager txManager, final ContextAware contextAware) {
return new JpaRolloutHandler(tenantAware, rolloutManagement, rolloutExecutor, lockRegistry, txManager, return new JpaRolloutHandler(tenantAware, rolloutManagement, rolloutExecutor, lockRegistry, txManager, contextAware);
contextAware);
} }
@Bean @Bean
@@ -777,8 +775,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
final ContextAware contextAware) { final ContextAware contextAware) {
return new JpaRolloutManagement(targetManagement, distributionSetManagement, eventPublisherHolder, return new JpaRolloutManagement(targetManagement, distributionSetManagement, eventPublisherHolder,
virtualPropertyReplacer, properties.getDatabase(), rolloutApprovalStrategy, virtualPropertyReplacer, properties.getDatabase(), rolloutApprovalStrategy,
tenantConfigurationManagement, systemSecurityContext, tenantConfigurationManagement, systemSecurityContext, contextAware);
contextAware);
} }
/** /**
@@ -860,10 +857,12 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
ArtifactManagement artifactManagement( ArtifactManagement artifactManagement(
final EntityManager entityManager, final LocalArtifactRepository localArtifactRepository, final EntityManager entityManager, final PlatformTransactionManager txManager,
final SoftwareModuleRepository softwareModuleRepository, final Optional<ArtifactRepository> artifactRepository, final LocalArtifactRepository localArtifactRepository, final SoftwareModuleRepository softwareModuleRepository,
final Optional<ArtifactRepository> artifactRepository,
final QuotaManagement quotaManagement, final TenantAware tenantAware) { final QuotaManagement quotaManagement, final TenantAware tenantAware) {
return new JpaArtifactManagement(entityManager, localArtifactRepository, softwareModuleRepository, artifactRepository.orElse(null), return new JpaArtifactManagement(
entityManager, txManager, localArtifactRepository, softwareModuleRepository, artifactRepository.orElse(null),
quotaManagement, tenantAware); quotaManagement, tenantAware);
} }

View File

@@ -13,15 +13,15 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.support.TransactionSynchronizationAdapter; import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionSynchronizationManager;
/** /**
* A Service which calls register runnable. This runnables will executed after a * A Service which calls register runnable. This runnables will be executed after a successful spring transaction commit.
* successful spring transaction commit.The class is thread safe. * The class is thread safe.
*/ */
@Slf4j @Slf4j
public class AfterTransactionCommitDefaultServiceExecutor extends TransactionSynchronizationAdapter implements AfterTransactionCommitExecutor { public class AfterTransactionCommitDefaultServiceExecutor implements TransactionSynchronization, AfterTransactionCommitExecutor {
private static final ThreadLocal<List<Runnable>> THREAD_LOCAL_RUNNABLES = new ThreadLocal<>(); private static final ThreadLocal<List<Runnable>> THREAD_LOCAL_RUNNABLES = new ThreadLocal<>();
@@ -30,6 +30,14 @@ public class AfterTransactionCommitDefaultServiceExecutor extends TransactionSyn
@SuppressWarnings({ "squid:S1217" }) @SuppressWarnings({ "squid:S1217" })
public void afterCommit() { public void afterCommit() {
final List<Runnable> afterCommitRunnables = THREAD_LOCAL_RUNNABLES.get(); final List<Runnable> afterCommitRunnables = THREAD_LOCAL_RUNNABLES.get();
if (afterCommitRunnables == null) {
log.trace("Transaction successfully committed, runnables is null");
return;
}
// removes the runnables that will process, so they would be able to start new transactions and
// inserting new after commit hooks
THREAD_LOCAL_RUNNABLES.remove();
log.debug("Transaction successfully committed, executing {} runnables", afterCommitRunnables.size()); log.debug("Transaction successfully committed, executing {} runnables", afterCommitRunnables.size());
for (final Runnable afterCommitRunnable : afterCommitRunnables) { for (final Runnable afterCommitRunnable : afterCommitRunnables) {
log.debug("Executing runnable {}", afterCommitRunnable); log.debug("Executing runnable {}", afterCommitRunnable);
@@ -44,8 +52,7 @@ public class AfterTransactionCommitDefaultServiceExecutor extends TransactionSyn
@Override @Override
@SuppressWarnings({ "squid:S1217" }) @SuppressWarnings({ "squid:S1217" })
public void afterCompletion(final int status) { public void afterCompletion(final int status) {
final String transactionStatus = status == STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK"; log.debug("Transaction completed after commit with status {}", status == STATUS_COMMITTED ? "COMMITTED" : "ROLLEDBACK");
log.debug("Transaction completed after commit with status {}", transactionStatus);
THREAD_LOCAL_RUNNABLES.remove(); THREAD_LOCAL_RUNNABLES.remove();
} }

View File

@@ -36,26 +36,31 @@ import org.eclipse.hawkbit.repository.exception.InvalidMD5HashException;
import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException; import org.eclipse.hawkbit.repository.exception.InvalidSHA1HashException;
import org.eclipse.hawkbit.repository.exception.InvalidSHA256HashException; import org.eclipse.hawkbit.repository.exception.InvalidSHA256HashException;
import org.eclipse.hawkbit.repository.jpa.EncryptionAwareDbArtifact; import org.eclipse.hawkbit.repository.jpa.EncryptionAwareDbArtifact;
import org.eclipse.hawkbit.repository.jpa.Jpa;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper; 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.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact; import org.eclipse.hawkbit.repository.jpa.model.JpaArtifact;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.jpa.repository.LocalArtifactRepository; import org.eclipse.hawkbit.repository.jpa.repository.LocalArtifactRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository; import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
import org.eclipse.hawkbit.repository.jpa.specifications.ArtifactSpecifications; import org.eclipse.hawkbit.repository.jpa.specifications.ArtifactSpecifications;
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
import org.eclipse.hawkbit.repository.jpa.utils.FileSizeAndStorageQuotaCheckingInputStream; import org.eclipse.hawkbit.repository.jpa.utils.FileSizeAndStorageQuotaCheckingInputStream;
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper; import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.ArtifactUpload; import org.eclipse.hawkbit.repository.model.ArtifactUpload;
import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.tenancy.TenantAware; import org.eclipse.hawkbit.tenancy.TenantAware;
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;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
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.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization; import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionSynchronizationManager;
@@ -70,6 +75,7 @@ import org.springframework.validation.annotation.Validated;
public class JpaArtifactManagement implements ArtifactManagement { public class JpaArtifactManagement implements ArtifactManagement {
private final EntityManager entityManager; private final EntityManager entityManager;
private final PlatformTransactionManager txManager;
private final LocalArtifactRepository localArtifactRepository; private final LocalArtifactRepository localArtifactRepository;
private final SoftwareModuleRepository softwareModuleRepository; private final SoftwareModuleRepository softwareModuleRepository;
@Nullable @Nullable
@@ -79,10 +85,13 @@ public class JpaArtifactManagement implements ArtifactManagement {
public JpaArtifactManagement( public JpaArtifactManagement(
final EntityManager entityManager, final EntityManager entityManager,
final PlatformTransactionManager txManager,
final LocalArtifactRepository localArtifactRepository, final LocalArtifactRepository localArtifactRepository,
final SoftwareModuleRepository softwareModuleRepository, @Nullable final ArtifactRepository artifactRepository, final SoftwareModuleRepository softwareModuleRepository, @Nullable final ArtifactRepository artifactRepository,
final QuotaManagement quotaManagement, final TenantAware tenantAware) { final QuotaManagement quotaManagement,
final TenantAware tenantAware) {
this.entityManager = entityManager; this.entityManager = entityManager;
this.txManager = txManager;
this.localArtifactRepository = localArtifactRepository; this.localArtifactRepository = localArtifactRepository;
this.softwareModuleRepository = softwareModuleRepository; this.softwareModuleRepository = softwareModuleRepository;
this.artifactRepository = artifactRepository; this.artifactRepository = artifactRepository;
@@ -137,18 +146,19 @@ public class JpaArtifactManagement implements ArtifactManagement {
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, @Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY)) backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void delete(final long id) { public void delete(final long id) {
final JpaArtifact toDelete = (JpaArtifact) get(id) final JpaArtifact toDelete = (JpaArtifact) get(id).orElseThrow(() -> new EntityNotFoundException(Artifact.class, id));
.orElseThrow(() -> new EntityNotFoundException(Artifact.class, id));
final JpaSoftwareModule softwareModule = toDelete.getSoftwareModule();
// clearArtifactBinary checks (unconditionally) software module UPDATE access // clearArtifactBinary checks (unconditionally) software module UPDATE access
softwareModuleRepository.getAccessController().ifPresent(accessController -> softwareModuleRepository.getAccessController().ifPresent(accessController ->
accessController.assertOperationAllowed(AccessController.Operation.UPDATE, accessController.assertOperationAllowed(AccessController.Operation.UPDATE, softwareModule));
(JpaSoftwareModule) toDelete.getSoftwareModule())); softwareModule.removeArtifact(toDelete);
((JpaSoftwareModule) toDelete.getSoftwareModule()).removeArtifact(toDelete); softwareModuleRepository.save(softwareModule);
softwareModuleRepository.save((JpaSoftwareModule) toDelete.getSoftwareModule());
localArtifactRepository.deleteById(id); localArtifactRepository.deleteById(id);
clearArtifactBinary(toDelete.getSha1Hash());
final String sha1Hash = toDelete.getSha1Hash();
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() -> clearArtifactBinary(sha1Hash));
} }
@Override @Override
@@ -211,39 +221,35 @@ public class JpaArtifactManagement implements ArtifactManagement {
} }
/** /**
* Garbage collects artifact binaries if only referenced by given * Garbage collects artifact binaries if only referenced by given {@link SoftwareModule#getId()} or {@link SoftwareModule}'s that are
* {@link SoftwareModule#getId()} or {@link SoftwareModule}'s that are
* marked as deleted. * marked as deleted.
* <p/> * <p/>
* Software module related UPDATE permission shall be checked by the callers! * Software module related UPDATE permission shall be checked by the callers!
* <p/>
* Note: Internal method. Shall be called ONLY if @PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
* has already been checked
* *
* @param sha1Hash no longer needed * @param sha1Hash no longer needed
*/ */
@PreAuthorize(SpPermission.SpringEvalExpressions.HAS_AUTH_DELETE_REPOSITORY)
void clearArtifactBinary(final String sha1Hash) { void clearArtifactBinary(final String sha1Hash) {
assertArtifactRepositoryAvailable(); assertArtifactRepositoryAvailable();
// countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse will skip ACM checks and DeploymentHelper.runInNewTransaction(txManager, "clearArtifactBinary", status -> {
// will return total count as it should be // countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse will skip ACM checks and will return total count as it should be
final long count = localArtifactRepository.countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse( if (localArtifactRepository.countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(sha1Hash, tenantAware.getCurrentTenant()) <= 0) { // 1 artifact is the one being deleted!
sha1Hash,
tenantAware.getCurrentTenant());
if (count <= 1) { // 1 artifact is the one being deleted!
// removes the real artifact ONLY AFTER the delete of artifact or software module // removes the real artifact ONLY AFTER the delete of artifact or software module
// in local history has passed successfully (caller has permission and no errors) // in local history has passed successfully (caller has permission and no errors)
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() -> {
@Override
public void afterCommit() {
try { try {
log.debug("deleting artifact from repository {}", sha1Hash); log.debug("deleting artifact from repository {}", sha1Hash);
artifactRepository.deleteBySha1(tenantAware.getCurrentTenant(), sha1Hash); artifactRepository.deleteBySha1(tenantAware.getCurrentTenant(), sha1Hash);
} catch (final ArtifactStoreException e) { } catch (final ArtifactStoreException e) {
throw new ArtifactDeleteFailedException(e); throw new ArtifactDeleteFailedException(e);
} }
}
}); });
} // else there are still other artifacts that need the binary } // else there are still other artifacts that need the binary
return null;
});
} }
private AbstractDbArtifact storeArtifact(final ArtifactUpload artifactUpload, final boolean isSmEncrypted) { private AbstractDbArtifact storeArtifact(final ArtifactUpload artifactUpload, final boolean isSmEncrypted) {

View File

@@ -48,6 +48,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata_; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleMetadata_;
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_; import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule_;
import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey; import org.eclipse.hawkbit.repository.jpa.model.SwMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository; import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepository; import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleMetadataRepository;
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository; import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
@@ -199,10 +200,6 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
final Set<Long> assignedModuleIds = new HashSet<>(); final Set<Long> assignedModuleIds = new HashSet<>();
swModulesToDelete.forEach(swModule -> { swModulesToDelete.forEach(swModule -> {
// delete binary data of artifacts
deleteGridFsArtifacts(swModule);
// execute this count operation without access limitations since we have to // execute this count operation without access limitations since we have to
// ensure it's not assigned when deleting it. // ensure it's not assigned when deleting it.
if (distributionSetRepository.countByModulesId(swModule.getId()) <= 0) { if (distributionSetRepository.countByModulesId(swModule.getId()) <= 0) {
@@ -210,6 +207,8 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
} else { } else {
assignedModuleIds.add(swModule.getId()); assignedModuleIds.add(swModule.getId());
} }
// schedule delete binary data of artifacts
deleteGridFsArtifacts(swModule);
}); });
if (!assignedModuleIds.isEmpty()) { if (!assignedModuleIds.isEmpty()) {
@@ -507,10 +506,9 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) { private void deleteGridFsArtifacts(final JpaSoftwareModule swModule) {
softwareModuleRepository.getAccessController().ifPresent(accessController -> softwareModuleRepository.getAccessController().ifPresent(accessController ->
accessController.assertOperationAllowed(AccessController.Operation.DELETE, swModule)); accessController.assertOperationAllowed(AccessController.Operation.DELETE, swModule));
for (final Artifact localArtifact : swModule.getArtifacts()) { final Set<String> sha1Hashes = swModule.getArtifacts().stream().map(Artifact::getSha1Hash).collect(Collectors.toSet());
((JpaArtifactManagement) artifactManagement) AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() ->
.clearArtifactBinary(localArtifact.getSha1Hash()); sha1Hashes.forEach(sha1Hash -> ((JpaArtifactManagement) artifactManagement).clearArtifactBinary(sha1Hash)));
}
} }
private Specification<JpaSoftwareModule> buildSmSearchQuerySpec(final String searchText) { private Specification<JpaSoftwareModule> buildSmSearchQuerySpec(final String searchText) {

View File

@@ -60,6 +60,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey; import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository; import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository; import org.eclipse.hawkbit.repository.jpa.repository.TargetFilterQueryRepository;
import org.eclipse.hawkbit.repository.jpa.repository.TargetMetadataRepository; import org.eclipse.hawkbit.repository.jpa.repository.TargetMetadataRepository;
@@ -648,15 +649,20 @@ public class JpaTargetManagement implements TargetManagement {
} }
@Override @Override
@Transactional
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void requestControllerAttributes(final String controllerId) { public void requestControllerAttributes(final String controllerId) {
final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId); final JpaTarget target = getByControllerIdAndThrowIfNotFound(controllerId);
targetRepository.getAccessController() targetRepository.getAccessController()
.ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target)); .ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target));
target.setRequestControllerAttributes(true); target.setRequestControllerAttributes(true);
AfterTransactionCommitExecutorHolder.getInstance().getAfterCommit().afterCommit(() ->
eventPublisherHolder.getEventPublisher() eventPublisherHolder.getEventPublisher()
.publishEvent(new TargetAttributesRequestedEvent(tenantAware.getCurrentTenant(), target.getId(), .publishEvent(new TargetAttributesRequestedEvent(
target.getControllerId(), target.getAddress() != null ? target.getAddress().toString() : null, tenantAware.getCurrentTenant(), target.getId(), target.getControllerId(),
JpaTarget.class, eventPublisherHolder.getApplicationId())); target.getAddress() != null ? target.getAddress().toString() : null,
JpaTarget.class, eventPublisherHolder.getApplicationId())));
} }
@Override @Override
@@ -718,14 +724,12 @@ public class JpaTargetManagement implements TargetManagement {
final JpaTarget updatedTarget = JpaManagementHelper.touch(entityManager, targetRepository, target); final JpaTarget updatedTarget = JpaManagementHelper.touch(entityManager, targetRepository, target);
final List<TargetMetadata> createdMetadata = md.stream() final List<TargetMetadata> createdMetadata = md.stream()
.map(meta -> targetMetadataRepository .map(meta -> targetMetadataRepository.save(new JpaTargetMetadata(meta.getKey(), meta.getValue(), updatedTarget)))
.save(new JpaTargetMetadata(meta.getKey(), meta.getValue(), updatedTarget)))
.collect(Collectors.toUnmodifiableList()); .collect(Collectors.toUnmodifiableList());
// TargetUpdatedEvent is not sent within the touch() method due to the // TargetUpdatedEvent is not sent within the touch() method due to the
// "lastModifiedAt" field being ignored in JpaTarget // "lastModifiedAt" field being ignored in JpaTarget
eventPublisherHolder.getEventPublisher() eventPublisherHolder.getEventPublisher().publishEvent(new TargetUpdatedEvent(updatedTarget, eventPublisherHolder.getApplicationId()));
.publishEvent(new TargetUpdatedEvent(updatedTarget, eventPublisherHolder.getApplicationId()));
return createdMetadata; return createdMetadata;
} }
@@ -738,8 +742,8 @@ public class JpaTargetManagement implements TargetManagement {
final JpaTargetMetadata metadata = (JpaTargetMetadata) getMetaDataByControllerId(controllerId, key) final JpaTargetMetadata metadata = (JpaTargetMetadata) getMetaDataByControllerId(controllerId, key)
.orElseThrow(() -> new EntityNotFoundException(TargetMetadata.class, controllerId, key)); .orElseThrow(() -> new EntityNotFoundException(TargetMetadata.class, controllerId, key));
final JpaTarget target = JpaManagementHelper.touch(entityManager, targetRepository, final JpaTarget target = JpaManagementHelper.touch(
getByControllerIdAndThrowIfNotFound(controllerId)); entityManager, targetRepository, getByControllerIdAndThrowIfNotFound(controllerId));
targetRepository.getAccessController() targetRepository.getAccessController()
.ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target)); .ifPresent(acm -> acm.assertOperationAllowed(AccessController.Operation.UPDATE, target));

View File

@@ -64,12 +64,12 @@ import org.junit.jupiter.api.Test;
*/ */
@Feature("Component Tests - Repository") @Feature("Component Tests - Repository")
@Story("Artifact Management") @Story("Artifact Management")
public class ArtifactManagementTest extends AbstractJpaIntegrationTest { class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that management get access react as specfied on calls for non existing entities by means of Optional not present.") @Description("Verifies that management get access react as specfied on calls for non existing entities by means of Optional not present.")
@ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) }) @ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) })
public void nonExistingEntityAccessReturnsNotPresent() { void nonExistingEntityAccessReturnsNotPresent() {
final SoftwareModule module = testdataFactory.createSoftwareModuleOs(); final SoftwareModule module = testdataFactory.createSoftwareModuleOs();
assertThat(artifactManagement.get(NOT_EXIST_IDL)).isNotPresent(); assertThat(artifactManagement.get(NOT_EXIST_IDL)).isNotPresent();
@@ -85,7 +85,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Description("Verifies that management queries react as specfied on calls for non existing entities " @Description("Verifies that management queries react as specfied on calls for non existing entities "
+ " by means of throwing EntityNotFoundException.") + " by means of throwing EntityNotFoundException.")
@ExpectEvents({ @Expect(type = SoftwareModuleDeletedEvent.class, count = 0) }) @ExpectEvents({ @Expect(type = SoftwareModuleDeletedEvent.class, count = 0) })
public void entityQueriesReferringToNotExistingEntitiesThrowsException() throws URISyntaxException { void entityQueriesReferringToNotExistingEntitiesThrowsException() throws URISyntaxException {
final String artifactData = "test"; final String artifactData = "test";
final int artifactSize = artifactData.length(); final int artifactSize = artifactData.length();
@@ -110,7 +110,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Test if a local artifact can be created by API including metadata.") @Description("Test if a local artifact can be created by API including metadata.")
public void createArtifact() throws IOException { void createArtifact() throws IOException {
// check baseline // check baseline
assertThat(softwareModuleRepository.findAll()).hasSize(0); assertThat(softwareModuleRepository.findAll()).hasSize(0);
@@ -161,7 +161,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that artifact management does not create artifacts with illegal filename.") @Description("Verifies that artifact management does not create artifacts with illegal filename.")
public void entityQueryWithIllegalFilenameThrowsException() throws URISyntaxException { void entityQueryWithIllegalFilenameThrowsException() throws URISyntaxException {
final String illegalFilename = "<img src=ernw onerror=alert(1)>.xml"; final String illegalFilename = "<img src=ernw onerror=alert(1)>.xml";
final String artifactData = "test"; final String artifactData = "test";
final int artifactSize = artifactData.length(); final int artifactSize = artifactData.length();
@@ -176,7 +176,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that the quota specifying the maximum number of artifacts per software module is enforced.") @Description("Verifies that the quota specifying the maximum number of artifacts per software module is enforced.")
public void createArtifactsUntilQuotaIsExceeded() throws IOException { void createArtifactsUntilQuotaIsExceeded() throws IOException {
// create a software module // create a software module
final long smId = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0")).getId(); final long smId = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0")).getId();
@@ -205,7 +205,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that the quota specifying the maximum artifact storage is enforced (across software modules).") @Description("Verifies that the quota specifying the maximum artifact storage is enforced (across software modules).")
public void createArtifactsUntilStorageQuotaIsExceeded() throws IOException { void createArtifactsUntilStorageQuotaIsExceeded() throws IOException {
// create as many small artifacts as possible w/o violating the storage // create as many small artifacts as possible w/o violating the storage
// quota // quota
@@ -235,7 +235,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that you cannot create artifacts which exceed the configured maximum size.") @Description("Verifies that you cannot create artifacts which exceed the configured maximum size.")
public void createArtifactFailsIfTooLarge() { void createArtifactFailsIfTooLarge() {
// create a software module // create a software module
final JpaSoftwareModule sm1 = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0")); final JpaSoftwareModule sm1 = softwareModuleRepository.save(new JpaSoftwareModule(osType, "sm1", "1.0"));
@@ -248,7 +248,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Tests hard delete directly on repository.") @Description("Tests hard delete directly on repository.")
public void hardDeleteSoftwareModule() throws IOException { void hardDeleteSoftwareModule() throws IOException {
final JpaSoftwareModule sm = softwareModuleRepository final JpaSoftwareModule sm = softwareModuleRepository
.save(new JpaSoftwareModule(osType, "name 1", "version 1")); .save(new JpaSoftwareModule(osType, "name 1", "version 1"));
@@ -268,7 +268,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
*/ */
@Test @Test
@Description("Tests the deletion of a local artifact including metadata.") @Description("Tests the deletion of a local artifact including metadata.")
public void deleteArtifact() throws IOException { void deleteArtifact() throws IOException {
final JpaSoftwareModule sm = softwareModuleRepository final JpaSoftwareModule sm = softwareModuleRepository
.save(new JpaSoftwareModule(osType, "name 1", "version 1")); .save(new JpaSoftwareModule(osType, "name 1", "version 1"));
final JpaSoftwareModule sm2 = softwareModuleRepository final JpaSoftwareModule sm2 = softwareModuleRepository
@@ -320,7 +320,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Test the deletion of an artifact metadata where the binary is still linked to another metadata element. " + @Description("Test the deletion of an artifact metadata where the binary is still linked to another metadata element. " +
"The expected result is that the metadata is deleted but the binary kept.") "The expected result is that the metadata is deleted but the binary kept.")
public void deleteDuplicateArtifacts() throws IOException { void deleteDuplicateArtifacts() throws IOException {
final JpaSoftwareModule sm = softwareModuleRepository final JpaSoftwareModule sm = softwareModuleRepository
.save(new JpaSoftwareModule(osType, "name 1", "version 1")); .save(new JpaSoftwareModule(osType, "name 1", "version 1"));
final JpaSoftwareModule sm2 = softwareModuleRepository final JpaSoftwareModule sm2 = softwareModuleRepository
@@ -353,7 +353,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that you cannot delete an artifact which exists with the same hash, in the same tenant and the SoftwareModule is not deleted .") @Description("Verifies that you cannot delete an artifact which exists with the same hash, in the same tenant and the SoftwareModule is not deleted .")
public void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInSameTenants() throws IOException { void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInSameTenants() throws IOException {
final JpaSoftwareModule sm = softwareModuleRepository final JpaSoftwareModule sm = softwareModuleRepository
.save(new JpaSoftwareModule(osType, "name 1", "version 1")); .save(new JpaSoftwareModule(osType, "name 1", "version 1"));
@@ -395,7 +395,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that you can not delete artifacts from another tenant which exists in another tenant with the same hash and the SoftwareModule is not deleted") @Description("Verifies that you can not delete artifacts from another tenant which exists in another tenant with the same hash and the SoftwareModule is not deleted")
public void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInDifferentTenants() throws Exception { void deleteArtifactWithSameHashAndSoftwareModuleIsNotDeletedInDifferentTenants() throws Exception {
final String tenant1 = "mytenant"; final String tenant1 = "mytenant";
final String tenant2 = "tenant2"; final String tenant2 = "tenant2";
@@ -422,7 +422,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Loads an local artifact based on given ID.") @Description("Loads an local artifact based on given ID.")
public void findArtifact() throws IOException { void findArtifact() throws IOException {
final int artifactSize = 5 * 1024; final int artifactSize = 5 * 1024;
try (final InputStream inputStream = new RandomGeneratedInputStream(artifactSize)) { try (final InputStream inputStream = new RandomGeneratedInputStream(artifactSize)) {
final Artifact artifact = createArtifactForSoftwareModule( final Artifact artifact = createArtifactForSoftwareModule(
@@ -433,7 +433,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Loads an artifact binary based on given ID.") @Description("Loads an artifact binary based on given ID.")
public void loadStreamOfArtifact() throws IOException { void loadStreamOfArtifact() throws IOException {
final int artifactSize = 5 * 1024; final int artifactSize = 5 * 1024;
final byte[] randomBytes = randomBytes(artifactSize); final byte[] randomBytes = randomBytes(artifactSize);
try (final InputStream input = new ByteArrayInputStream(randomBytes)) { try (final InputStream input = new ByteArrayInputStream(randomBytes)) {
@@ -448,7 +448,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@WithUser(allSpPermissions = true, removeFromAllPermission = { SpPermission.DOWNLOAD_REPOSITORY_ARTIFACT }) @WithUser(allSpPermissions = true, removeFromAllPermission = { SpPermission.DOWNLOAD_REPOSITORY_ARTIFACT })
@Description("Trys and fails to load an artifact without required permission. Checks if expected InsufficientPermissionException is thrown.") @Description("Trys and fails to load an artifact without required permission. Checks if expected InsufficientPermissionException is thrown.")
public void loadArtifactBinaryWithoutDownloadArtifactThrowsPermissionDenied() { void loadArtifactBinaryWithoutDownloadArtifactThrowsPermissionDenied() {
assertThatExceptionOfType(InsufficientPermissionException.class) assertThatExceptionOfType(InsufficientPermissionException.class)
.as("Should not have worked with missing permission.") .as("Should not have worked with missing permission.")
.isThrownBy(() -> artifactManagement.loadArtifactBinary("123", 1, false)); .isThrownBy(() -> artifactManagement.loadArtifactBinary("123", 1, false));
@@ -456,7 +456,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Searches an artifact through the relations of a software module.") @Description("Searches an artifact through the relations of a software module.")
public void findArtifactBySoftwareModule() throws IOException { void findArtifactBySoftwareModule() throws IOException {
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
assertThat(artifactManagement.findBySoftwareModule(PAGE, sm.getId())).isEmpty(); assertThat(artifactManagement.findBySoftwareModule(PAGE, sm.getId())).isEmpty();
@@ -469,7 +469,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Searches an artifact through the relations of a software module and the filename.") @Description("Searches an artifact through the relations of a software module and the filename.")
public void findByFilenameAndSoftwareModule() throws IOException { void findByFilenameAndSoftwareModule() throws IOException {
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
assertThat(artifactManagement.getByFilenameAndSoftwareModule("file1", sm.getId())).isNotPresent(); assertThat(artifactManagement.getByFilenameAndSoftwareModule("file1", sm.getId())).isNotPresent();
@@ -485,7 +485,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that creation of an artifact with none matching hashes fails.") @Description("Verifies that creation of an artifact with none matching hashes fails.")
public void createArtifactWithNoneMatchingHashes() throws IOException, NoSuchAlgorithmException { void createArtifactWithNoneMatchingHashes() throws IOException, NoSuchAlgorithmException {
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
final byte[] testData = RandomStringUtils.randomAlphanumeric(100).getBytes(); final byte[] testData = RandomStringUtils.randomAlphanumeric(100).getBytes();
@@ -517,7 +517,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that creation of an artifact with matching hashes works.") @Description("Verifies that creation of an artifact with matching hashes works.")
public void createArtifactWithMatchingHashes() throws IOException, NoSuchAlgorithmException { void createArtifactWithMatchingHashes() throws IOException, NoSuchAlgorithmException {
final SoftwareModule sm = testdataFactory.createSoftwareModuleOs(); final SoftwareModule sm = testdataFactory.createSoftwareModuleOs();
final byte[] testData = RandomStringUtils.randomAlphanumeric(100).getBytes(); final byte[] testData = RandomStringUtils.randomAlphanumeric(100).getBytes();
@@ -540,7 +540,7 @@ public class ArtifactManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that creation of an existing artifact returns a full hash list.") @Description("Verifies that creation of an existing artifact returns a full hash list.")
public void createExistingArtifactReturnsFullHashList() throws IOException, NoSuchAlgorithmException { void createExistingArtifactReturnsFullHashList() throws IOException, NoSuchAlgorithmException {
final SoftwareModule smOs = testdataFactory.createSoftwareModuleOs(); final SoftwareModule smOs = testdataFactory.createSoftwareModuleOs();
final SoftwareModule smApp = testdataFactory.createSoftwareModuleApp(); final SoftwareModule smApp = testdataFactory.createSoftwareModuleApp();

View File

@@ -57,13 +57,13 @@ import org.springframework.data.domain.PageRequest;
@Feature("Component Tests - Repository") @Feature("Component Tests - Repository")
@Story("Software Module Management") @Story("Software Module Management")
public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that management get access reacts as specified on calls for non existing entities by means " @Description("Verifies that management get access reacts as specified on calls for non existing entities by means "
+ "of Optional not present.") + "of Optional not present.")
@ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) }) @ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) })
public void nonExistingEntityAccessReturnsNotPresent() { void nonExistingEntityAccessReturnsNotPresent() {
final SoftwareModule module = testdataFactory.createSoftwareModuleApp(); final SoftwareModule module = testdataFactory.createSoftwareModuleApp();
assertThat(softwareModuleManagement.get(1234L)).isNotPresent(); assertThat(softwareModuleManagement.get(1234L)).isNotPresent();
@@ -78,7 +78,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Description("Verifies that management queries react as specfied on calls for non existing entities " @Description("Verifies that management queries react as specfied on calls for non existing entities "
+ " by means of throwing EntityNotFoundException.") + " by means of throwing EntityNotFoundException.")
@ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) }) @ExpectEvents({ @Expect(type = SoftwareModuleCreatedEvent.class, count = 1) })
public void entityQueriesReferringToNotExistingEntitiesThrowsException() { void entityQueriesReferringToNotExistingEntitiesThrowsException() {
final SoftwareModule module = testdataFactory.createSoftwareModuleApp(); final SoftwareModule module = testdataFactory.createSoftwareModuleApp();
verifyThrownExceptionBy( verifyThrownExceptionBy(
@@ -138,7 +138,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Calling update without changing fields results in no recorded change in the repository including unchanged audit fields.") @Description("Calling update without changing fields results in no recorded change in the repository including unchanged audit fields.")
public void updateNothingResultsInUnchangedRepository() { void updateNothingResultsInUnchangedRepository() {
final SoftwareModule ah = testdataFactory.createSoftwareModuleOs(); final SoftwareModule ah = testdataFactory.createSoftwareModuleOs();
final SoftwareModule updated = softwareModuleManagement final SoftwareModule updated = softwareModuleManagement
@@ -151,7 +151,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Calling update for changed fields results in change in the repository.") @Description("Calling update for changed fields results in change in the repository.")
public void updateSoftwareModuleFieldsToNewValue() { void updateSoftwareModuleFieldsToNewValue() {
final SoftwareModule ah = testdataFactory.createSoftwareModuleOs(); final SoftwareModule ah = testdataFactory.createSoftwareModuleOs();
final SoftwareModule updated = softwareModuleManagement final SoftwareModule updated = softwareModuleManagement
@@ -166,7 +166,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Create Software Module call fails when called for existing entity.") @Description("Create Software Module call fails when called for existing entity.")
public void createModuleCallFailsForExistingModule() { void createModuleCallFailsForExistingModule() {
testdataFactory.createSoftwareModuleOs(); testdataFactory.createSoftwareModuleOs();
assertThatExceptionOfType(EntityAlreadyExistsException.class) assertThatExceptionOfType(EntityAlreadyExistsException.class)
.as("Should not have worked as module already exists.") .as("Should not have worked as module already exists.")
@@ -175,7 +175,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("searched for software modules based on the various filter options, e.g. name,desc,type, version.") @Description("searched for software modules based on the various filter options, e.g. name,desc,type, version.")
public void findSoftwareModuleByFilters() { void findSoftwareModuleByFilters() {
final SoftwareModule ah = softwareModuleManagement final SoftwareModule ah = softwareModuleManagement
.create(entityFactory.softwareModule().create().type(appType).name("agent-hub").version("1.0.1")); .create(entityFactory.softwareModule().create().type(appType).name("agent-hub").version("1.0.1"));
final SoftwareModule jvm = softwareModuleManagement final SoftwareModule jvm = softwareModuleManagement
@@ -217,7 +217,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Searches for software modules based on a list of IDs.") @Description("Searches for software modules based on a list of IDs.")
public void findSoftwareModulesById() { void findSoftwareModulesById() {
final List<Long> modules = Arrays.asList(testdataFactory.createSoftwareModuleOs().getId(), final List<Long> modules = Arrays.asList(testdataFactory.createSoftwareModuleOs().getId(),
testdataFactory.createSoftwareModuleApp().getId(), 624355263L); testdataFactory.createSoftwareModuleApp().getId(), 624355263L);
@@ -227,7 +227,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Searches for software modules by type.") @Description("Searches for software modules by type.")
public void findSoftwareModulesByType() { void findSoftwareModulesByType() {
// found in test // found in test
final SoftwareModule one = testdataFactory.createSoftwareModuleOs("one"); final SoftwareModule one = testdataFactory.createSoftwareModuleOs("one");
final SoftwareModule two = testdataFactory.createSoftwareModuleOs("two"); final SoftwareModule two = testdataFactory.createSoftwareModuleOs("two");
@@ -242,7 +242,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Counts all software modules in the repsitory that are not marked as deleted.") @Description("Counts all software modules in the repsitory that are not marked as deleted.")
public void countSoftwareModulesAll() { void countSoftwareModulesAll() {
// found in test // found in test
testdataFactory.createSoftwareModuleOs("one"); testdataFactory.createSoftwareModuleOs("one");
testdataFactory.createSoftwareModuleOs("two"); testdataFactory.createSoftwareModuleOs("two");
@@ -256,7 +256,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Deletes an artifact, which is not assigned to a Distribution Set") @Description("Deletes an artifact, which is not assigned to a Distribution Set")
public void hardDeleteOfNotAssignedArtifact() { void hardDeleteOfNotAssignedArtifact() {
// [STEP1]: Create SoftwareModuleX with Artifacts // [STEP1]: Create SoftwareModuleX with Artifacts
final SoftwareModule unassignedModule = createSoftwareModuleWithArtifacts(osType, "moduleX", "3.0.2", 2); final SoftwareModule unassignedModule = createSoftwareModuleWithArtifacts(osType, "moduleX", "3.0.2", 2);
@@ -282,8 +282,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Deletes an artifact, which is assigned to a DistributionSet") @Description("Deletes an artifact, which is assigned to a DistributionSet")
public void softDeleteOfAssignedArtifact() { void softDeleteOfAssignedArtifact() {
// [STEP1]: Create SoftwareModuleX with ArtifactX // [STEP1]: Create SoftwareModuleX with ArtifactX
SoftwareModule assignedModule = createSoftwareModuleWithArtifacts(osType, "moduleX", "3.0.2", 2); SoftwareModule assignedModule = createSoftwareModuleWithArtifacts(osType, "moduleX", "3.0.2", 2);
@@ -315,7 +314,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Delete an artifact, which has been assigned to a rolled out DistributionSet in the past") @Description("Delete an artifact, which has been assigned to a rolled out DistributionSet in the past")
public void softDeleteOfHistoricalAssignedArtifact() { void softDeleteOfHistoricalAssignedArtifact() {
// Init target // Init target
final Target target = testdataFactory.createTarget(); final Target target = testdataFactory.createTarget();
@@ -355,7 +354,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Delete an software module with an artifact, which is also used by another software module.") @Description("Delete an software module with an artifact, which is also used by another software module.")
public void deleteSoftwareModulesWithSharedArtifact() { void deleteSoftwareModulesWithSharedArtifact() {
// Init artifact binary data, target and DistributionSets // Init artifact binary data, target and DistributionSets
final int artifactSize = 1024; final int artifactSize = 1024;
@@ -401,7 +400,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Delete two assigned softwaremodules which share an artifact.") @Description("Delete two assigned softwaremodules which share an artifact.")
public void deleteMultipleSoftwareModulesWhichShareAnArtifact() { void deleteMultipleSoftwareModulesWhichShareAnArtifact() {
// Init artifact binary data, target and DistributionSets // Init artifact binary data, target and DistributionSets
final int artifactSize = 1024; final int artifactSize = 1024;
final byte[] source = RandomUtils.nextBytes(artifactSize); final byte[] source = RandomUtils.nextBytes(artifactSize);
@@ -460,7 +459,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that all undeleted software modules are found in the repository.") @Description("Verifies that all undeleted software modules are found in the repository.")
public void countSoftwareModuleTypesAll() { void countSoftwareModuleTypesAll() {
testdataFactory.createSoftwareModuleOs(); testdataFactory.createSoftwareModuleOs();
// one soft deleted // one soft deleted
@@ -474,7 +473,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that software modules are returned that are assigned to given DS.") @Description("Verifies that software modules are returned that are assigned to given DS.")
public void findSoftwareModuleByAssignedTo() { void findSoftwareModuleByAssignedTo() {
// test modules // test modules
final SoftwareModule one = testdataFactory.createSoftwareModuleOs(); final SoftwareModule one = testdataFactory.createSoftwareModuleOs();
testdataFactory.createSoftwareModuleOs("notassigned"); testdataFactory.createSoftwareModuleOs("notassigned");
@@ -491,7 +490,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Checks that metadata for a software module can be created.") @Description("Checks that metadata for a software module can be created.")
public void createSoftwareModuleMetadata() { void createSoftwareModuleMetadata() {
final String knownKey1 = "myKnownKey1"; final String knownKey1 = "myKnownKey1";
final String knownValue1 = "myKnownValue1"; final String knownValue1 = "myKnownValue1";
@@ -523,7 +522,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies the enforcement of the metadata quota per software module.") @Description("Verifies the enforcement of the metadata quota per software module.")
public void createSoftwareModuleMetadataUntilQuotaIsExceeded() { void createSoftwareModuleMetadataUntilQuotaIsExceeded() {
// add meta data one by one // add meta data one by one
final SoftwareModule module = testdataFactory.createSoftwareModuleApp("m1"); final SoftwareModule module = testdataFactory.createSoftwareModuleApp("m1");
@@ -569,7 +568,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Checks that metadata for a software module cannot be created for an existing key.") @Description("Checks that metadata for a software module cannot be created for an existing key.")
public void createSoftwareModuleMetadataFailsIfKeyExists() { void createSoftwareModuleMetadataFailsIfKeyExists() {
final String knownKey1 = "myKnownKey1"; final String knownKey1 = "myKnownKey1";
final String knownValue1 = "myKnownValue1"; final String knownValue1 = "myKnownValue1";
@@ -597,7 +596,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@WithUser(allSpPermissions = true) @WithUser(allSpPermissions = true)
@Description("Checks that metadata for a software module can be updated.") @Description("Checks that metadata for a software module can be updated.")
public void updateSoftwareModuleMetadata() { void updateSoftwareModuleMetadata() {
final String knownKey = "myKnownKey"; final String knownKey = "myKnownKey";
final String knownValue = "myKnownValue"; final String knownValue = "myKnownValue";
final String knownUpdateValue = "myNewUpdatedValue"; final String knownUpdateValue = "myNewUpdatedValue";
@@ -637,7 +636,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that existing metadata can be deleted.") @Description("Verifies that existing metadata can be deleted.")
public void deleteSoftwareModuleMetadata() { void deleteSoftwareModuleMetadata() {
final String knownKey1 = "myKnownKey1"; final String knownKey1 = "myKnownKey1";
final String knownValue1 = "myKnownValue1"; final String knownValue1 = "myKnownValue1";
@@ -660,7 +659,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Verifies that non existing metadata find results in exception.") @Description("Verifies that non existing metadata find results in exception.")
public void findSoftwareModuleMetadataFailsIfEntryDoesNotExist() { void findSoftwareModuleMetadataFailsIfEntryDoesNotExist() {
final String knownKey1 = "myKnownKey1"; final String knownKey1 = "myKnownKey1";
final String knownValue1 = "myKnownValue1"; final String knownValue1 = "myKnownValue1";
@@ -674,7 +673,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Queries and loads the metadata related to a given software module.") @Description("Queries and loads the metadata related to a given software module.")
public void findAllSoftwareModuleMetadataBySwId() { void findAllSoftwareModuleMetadataBySwId() {
final SoftwareModule sw1 = testdataFactory.createSoftwareModuleApp(); final SoftwareModule sw1 = testdataFactory.createSoftwareModuleApp();
final int metadataCountSw1 = 8; final int metadataCountSw1 = 8;