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 9dbf93d20..5d4e4b0b1 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
@@ -144,6 +144,17 @@ public interface DistributionSetManagement
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
void lock(final long id);
+ /**
+ * Unlocks a distribution set.
+ * Use it with extreme care! In general once distribution set is locked
+ * it shall not be unlocked. Note that it could have been assigned / deployed 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 unlock(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 dc7fa41e9..b305fc7b7 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
@@ -336,6 +336,17 @@ public interface SoftwareModuleManagement
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
void lock(long id);
+ /**
+ * Unlocks a software module.
+ * Use it with extreme care! In general once software module is locked
+ * it shall not be unlocked. Note that it could have been assigned / deployed to targets.
+ *
+ * @param id the software module id
+ * @throws EntityNotFoundException if software module with given ID does not exist
+ */
+ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
+ void unlock(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 3aa14e13b..eaab8dfd2 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
@@ -710,6 +710,18 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
}
}
+ @Override
+ @Transactional
+ @Retryable(include = {
+ ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ public void unlock(final long id) {
+ final JpaDistributionSet distributionSet = getById(id);
+ if (distributionSet.isLocked()) {
+ distributionSet.unlock();
+ 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 2815fb867..b67dc897b 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
@@ -633,6 +633,20 @@ public class JpaSoftwareModuleManagement implements SoftwareModuleManagement {
}
}
+ @Override
+ @Transactional
+ @Retryable(include = {
+ ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
+ public void unlock(final long id) {
+ final JpaSoftwareModule softwareModule = softwareModuleRepository
+ .findById(id)
+ .orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, id));
+ if (softwareModule.isLocked()) {
+ softwareModule.unlock();
+ softwareModuleRepository.save(softwareModule);
+ }
+ }
+
@Override
@Transactional
@Retryable(include = {
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java
index 43a7e72eb..c70717756 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaDistributionSet.java
@@ -268,6 +268,10 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
locked = true;
}
+ public void unlock() {
+ locked = false;
+ }
+
public void setDeleted(final boolean deleted) {
this.deleted = deleted;
}
diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java
index 6827ce62e..3c366e3ad 100644
--- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java
+++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaSoftwareModule.java
@@ -177,6 +177,10 @@ public class JpaSoftwareModule extends AbstractJpaNamedVersionedEntity implement
locked = true;
}
+ public void unlock() {
+ locked = false;
+ }
+
/**
* Marks or un-marks this software module as deleted.
*
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 205e82bd7..68f1fd378 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
@@ -1008,6 +1008,30 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
distributionSetManagement.get(distributionSet.getId()).map(DistributionSet::isLocked)
.orElse(false))
.isTrue();
+ // assert software modules are locked
+ assertThat(distributionSet.getModules().size()).isNotEqualTo(0);
+ distributionSetManagement.get(distributionSet.getId()).map(DistributionSet::getModules)
+ .orElseThrow().forEach(module -> assertThat(module.isLocked()).isTrue());
+ }
+
+ @Test
+ @Description("Unlocks a DS.")
+ void unlockDistributionSet() {
+ final DistributionSet distributionSet = testdataFactory.createDistributionSet("ds-1");
+ distributionSetManagement.lock(distributionSet.getId());
+ assertThat(
+ distributionSetManagement.get(distributionSet.getId()).map(DistributionSet::isLocked)
+ .orElse(false))
+ .isTrue();
+ distributionSetManagement.unlock(distributionSet.getId());
+ assertThat(
+ distributionSetManagement.get(distributionSet.getId()).map(DistributionSet::isLocked)
+ .orElse(true))
+ .isFalse();
+ // assert software modules are not unlocked
+ assertThat(distributionSet.getModules().size()).isNotEqualTo(0);
+ distributionSetManagement.get(distributionSet.getId()).map(DistributionSet::getModules)
+ .orElseThrow().forEach(module -> assertThat(module.isLocked()).isTrue());
}
@Test
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 8e9fb5e20..75c898b15 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
@@ -839,6 +839,20 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest {
.isTrue();
}
+ @Test
+ @Description("Unlocks a SM.")
+ void unlockSoftwareModule() {
+ final SoftwareModule softwareModule = testdataFactory.createSoftwareModule("sm-1");
+ softwareModuleManagement.lock(softwareModule.getId());
+ assertThat(
+ softwareModuleManagement.get(softwareModule.getId()).map(SoftwareModule::isLocked).orElse(false))
+ .isTrue();
+ softwareModuleManagement.unlock(softwareModule.getId());
+ assertThat(
+ softwareModuleManagement.get(softwareModule.getId()).map(SoftwareModule::isLocked).orElse(true))
+ .isFalse();
+ }
+
@Test
@Description("Artifacts of a locked SM can't be modified. Expected behaviour is to throw an exception and to do not modify them.")
void lockSoftwareModuleApplied() {