From e53542006520f84fa4af113181667d392b3e5462 Mon Sep 17 00:00:00 2001 From: Avgustin Marinov Date: Thu, 15 Feb 2024 10:35:25 +0200 Subject: [PATCH] [#1580] Software Module & Distribution Set lock: add lock at mgmt level (2) (#1645) * Added to management intefaces (+ tests) Signed-off-by: Marinov Avgustin --- .../repository/DistributionSetManagement.java | 10 +++++++ .../repository/SoftwareModuleManagement.java | 13 ++++++-- .../JpaDistributionSetManagement.java | 12 ++++++++ .../JpaSoftwareModuleManagement.java | 14 +++++++++ .../DistributionSetManagementTest.java | 30 ++++++++++++++++++- .../SoftwareModuleManagementTest.java | 13 ++++++++ 6 files changed, 88 insertions(+), 4 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java index 506007158..9dbf93d20 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DistributionSetManagement.java @@ -134,6 +134,16 @@ public interface DistributionSetManagement @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) void deleteMetaData(long id, @NotEmpty String key); + /** + * Locks a distribution set. From then on its functional properties could not be changed and + * it could be assigned to targets + * + * @param id the distribution set id + * @throws EntityNotFoundException if distribution set with given ID does not exist + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) + void lock(final long id); + /** * Retrieves the distribution set for a given action. * diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java index d5a31ed5f..dc7fa41e9 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/SoftwareModuleManagement.java @@ -322,13 +322,20 @@ public interface SoftwareModuleManagement * to be filtered on * * @return the found {@link SoftwareModule}s - * - * @throws EntityNotFoundException - * if software module type with given ID does not exist + * @throws EntityNotFoundException if software module type with given ID does not exist */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY) Slice findByType(@NotNull Pageable pageable, long typeId); + /** + * Locks a software module. + * + * @param id the software module id + * @throws EntityNotFoundException if software module with given ID does not exist + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY) + void lock(long id); + /** * Updates a distribution set meta data value if corresponding entry exists. * diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java index e96468d07..cd5f4d2f4 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetManagement.java @@ -686,6 +686,18 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { return result; } + @Override + @Transactional + @Retryable(include = { + ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) + public void lock(final long id) { + final JpaDistributionSet distributionSet = getById(id); + if (!distributionSet.isLocked()) { + distributionSet.lock(); + distributionSetRepository.save(distributionSet); + } + } + @Override @Transactional @Retryable(include = { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java index 96a7d533d..6fa715c2b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaSoftwareModuleManagement.java @@ -616,6 +616,20 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement { .map(SoftwareModuleMetadata.class::cast); } + @Override + @Transactional + @Retryable(include = { + ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) + public void lock(final long id) { + final JpaSoftwareModule softwareModule = softwareModuleRepository + .findById(id) + .orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id)); + if (!softwareModule.isLocked()) { + softwareModule.lock(); + softwareModuleRepository.save(softwareModule); + } + } + @Override @Transactional @Retryable(include = { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetManagementTest.java index b983c169e..946762652 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetManagementTest.java @@ -995,7 +995,35 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest { } @Test - @Description("Deltes a DS that is no in use. Expected behaviour is a hard delete on the database.") + @Description("Locks a DS.") + void lockDistributionSet() { + final DistributionSet distributionSet = testdataFactory.createDistributionSet("ds-1"); + assertThat( + distributionSetManagement.get(distributionSet.getId()).map(DistributionSet::isLocked) + .orElse(true)) + .isFalse(); + distributionSetManagement.lock(distributionSet.getId()); + assertThat( + distributionSetManagement.get(distributionSet.getId()).map(DistributionSet::isLocked) + .orElse(false)) + .isTrue(); + } + + @Test + @Description("Locks an incomplete DS. Expected behaviour is to throw exception and to do not lock it.") + void lockIncompleteDistributionSetFails() { + final DistributionSet incompleteDistributionSet = testdataFactory.createIncompleteDistributionSet(); + assertThatExceptionOfType(IncompleteDistributionSetException.class) + .as("Locking an incomplete distribution set should throw an exception") + .isThrownBy(() -> distributionSetManagement.lock(incompleteDistributionSet.getId())); + assertThat( + distributionSetManagement.get(incompleteDistributionSet.getId()).map(DistributionSet::isLocked) + .orElse(true)) + .isFalse(); + } + + @Test + @Description("Deletes a DS that is no in use. Expected behaviour is a hard delete on the database.") void deleteUnassignedDistributionSet() { final DistributionSet ds1 = testdataFactory.createDistributionSet("ds-1"); testdataFactory.createDistributionSet("ds-2"); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareModuleManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareModuleManagementTest.java index 7bc58e6b4..66db1c2a9 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareModuleManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/SoftwareModuleManagementTest.java @@ -821,6 +821,19 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { .getContent()).as("Metadata elements are").isEmpty(); } + @Test + @Description("Locks a SM.") + void lockSoftwareModule() { + final SoftwareModule softwareModule = testdataFactory.createSoftwareModule("ds-1"); + assertThat( + softwareModuleManagement.get(softwareModule.getId()).map(SoftwareModule::isLocked).orElse(true)) + .isFalse(); + softwareModuleManagement.lock(softwareModule.getId()); + assertThat( + softwareModuleManagement.get(softwareModule.getId()).map(SoftwareModule::isLocked).orElse(false)) + .isTrue(); + } + @Test @Description("Verifies that non existing metadata find results in exception.") public void findSoftwareModuleMetadataFailsIfEntryDoesNotExist() {