diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java index e3ff4a8cf..6abcbe1e2 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java @@ -206,9 +206,14 @@ public enum SpServerError { SP_DS_INCOMPLETE("hawkbit.server.error.distributionset.incomplete", "Distribution set is assigned/locked to a target that is incomplete (i.e. mandatory modules are missing)"), /** - * + * error message, which describes that an entity is locked and can't be functionally modified */ - SP_LOCKED("hawkbit.server.error.locked", "Entry is locked. Could not be modified"), + SP_LOCKED("hawkbit.server.error.locked", "Entry is locked. Could not be functionally modified"), + + /** + * error message, which describes that an entity is locked and can't be functionally modified + */ + SP_DELETED("hawkbit.server.error.deleted", "Entry is soft deleted"), /** * diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java index d3ce100b3..fcabe5e25 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java @@ -51,6 +51,7 @@ import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.RolloutCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupUpdatedEvent; @@ -93,9 +94,10 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) }) public void sendDownloadAndInstallStatus() { final String controllerId = TARGET_PREFIX + "sendDownloadAndInstallStatus"; @@ -110,9 +112,10 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) }) public void sendDownloadStatusBeforeMaintenanceWindowStartTime() { final String controllerId = TARGET_PREFIX + "sendDownloadStatusBeforeWindowStartTime"; @@ -132,11 +135,12 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) }) - public void sendDownloadAndInstallStatusMessageDuringMaintenanceWindow() { + public void sendDownloadAndInstallStatusMessageDuringMaintenanceWindow() { final String controllerId = TARGET_PREFIX + "sendDAndIStatusMessageDuringWindow"; registerAndAssertTargetWithExistingTenant(controllerId); @@ -155,9 +159,10 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @Expect(type = TargetAssignDistributionSetEvent.class, count = 2), @Expect(type = CancelTargetAssignmentEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 2), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 12), @Expect(type = DistributionSetCreatedEvent.class, count = 2), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 18), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 3) }) public void assignDistributionSetMultipleTimes() { final String controllerId = TARGET_PREFIX + "assignDistributionSetMultipleTimes"; @@ -198,8 +203,10 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = CancelTargetAssignmentEvent.class, count = 0), @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 0), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 2), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) public void assignMultipleDsInMultiAssignMode() { @@ -283,8 +290,10 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = CancelTargetAssignmentEvent.class, count = 0), @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 2), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 2), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) public void cancelActionInMultiAssignMode() { @@ -316,8 +325,10 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = CancelTargetAssignmentEvent.class, count = 0), @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 2), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 3), @Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) public void finishActionInMultiAssignMode() { @@ -343,8 +354,10 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = CancelTargetAssignmentEvent.class, count = 0), @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 0), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) public void assignDsMultipleTimesInMultiAssignMode() { @@ -388,8 +401,10 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = CancelTargetAssignmentEvent.class, count = 0), @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 2), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1), @Expect(type = RolloutCreatedEvent.class, count = 2), @Expect(type = RolloutUpdatedEvent.class, count = 6), @Expect(type = RolloutGroupCreatedEvent.class, count = 2), @@ -418,8 +433,10 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = MultiActionAssignEvent.class, count = 3), @Expect(type = ActionCreatedEvent.class, count = 3), @Expect(type = ActionUpdatedEvent.class, count = 5), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 2), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 5), @Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TargetAttributesRequestedEvent.class, count = 2), @Expect(type = RolloutCreatedEvent.class, count = 3), @Expect(type = RolloutUpdatedEvent.class, count = 9), @@ -475,9 +492,10 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = CancelTargetAssignmentEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 2) }) public void sendCancelStatus() { final String controllerId = TARGET_PREFIX + "sendCancelStatus"; @@ -506,8 +524,10 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = ActionCreatedEvent.class, count = 2), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 2), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 4), @Expect(type = TargetAttributesRequestedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) }) @@ -531,9 +551,10 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) }) public void downloadOnlyAssignmentSendsDownloadMessageTopic() { final String controllerId = TARGET_PREFIX + "registerTargets_1"; @@ -640,9 +661,10 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 3), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 3), @Expect(type = ActionUpdatedEvent.class, count = 0), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 3), @Expect(type = TargetPollEvent.class, count = 3), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) void assertBatchAssignmentsMessagePayload(final EventTopic topic) { @@ -698,9 +720,10 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageHandlerServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageHandlerServiceIntegrationTest.java index 488973239..f626d3f50 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageHandlerServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageHandlerServiceIntegrationTest.java @@ -44,6 +44,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; @@ -371,9 +372,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetAttributesRequestedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) }) void finishActionStatus() { @@ -386,9 +388,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) }) void runningActionStatus() { final String controllerId = TARGET_PREFIX + "runningActionStatus"; @@ -400,9 +403,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) }) void downloadedActionStatus() { final String controllerId = TARGET_PREFIX + "downloadedActionStatus"; @@ -414,9 +418,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) }) void downloadActionStatus() { final String controllerId = TARGET_PREFIX + "downloadActionStatus"; @@ -428,9 +433,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) }) void errorActionStatus() { final String controllerId = TARGET_PREFIX + "errorActionStatus"; @@ -442,9 +448,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) }) void warningActionStatus() { final String controllerId = TARGET_PREFIX + "warningActionStatus"; @@ -456,9 +463,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) }) void retrievedActionStatus() { final String controllerId = TARGET_PREFIX + "retrievedActionStatus"; @@ -470,9 +478,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) }) void cancelNotAllowActionStatus() { final String controllerId = TARGET_PREFIX + "cancelNotAllowActionStatus"; @@ -485,9 +494,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 2) }) void receiveDownloadAndInstallMessageAfterAssignment() { final String controllerId = TARGET_PREFIX + "receiveDownLoadAndInstallMessageAfterAssignment"; @@ -511,9 +521,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 2) }) void receiveDownloadMessageBeforeMaintenanceWindowStartTime() { final String controllerId = TARGET_PREFIX + "receiveDownLoadMessageBeforeMaintenanceWindowStartTime"; @@ -538,9 +549,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 2) }) void receiveDownloadAndInstallMessageDuringMaintenanceWindow() { final String controllerId = TARGET_PREFIX + "receiveDownLoadAndInstallMessageDuringMaintenanceWindow"; @@ -565,8 +577,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = CancelTargetAssignmentEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 2) }) @@ -593,10 +607,11 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), - @Expect(type = CancelTargetAssignmentEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock + @Expect(type = CancelTargetAssignmentEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) }) void actionNotExists() { final String controllerId = TARGET_PREFIX + "actionNotExists"; @@ -613,9 +628,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) }) void canceledRejectedNotAllowActionStatus() { final String controllerId = TARGET_PREFIX + "canceledRejectedNotAllowActionStatus"; @@ -629,9 +645,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = CancelTargetAssignmentEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) }) void canceledRejectedActionStatus() { final String controllerId = TARGET_PREFIX + "canceledRejectedActionStatus"; @@ -811,9 +828,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetAttributesRequestedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) }) void downloadOnlyAssignmentFinishesActionWhenTargetReportsDownloaded() throws IOException { @@ -841,9 +859,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetAttributesRequestedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 3), @Expect(type = TargetPollEvent.class, count = 1) }) void downloadOnlyAssignmentAllowsActionStatusUpdatesWhenTargetReportsFinishedAndUpdatesInstalledDS() @@ -1029,9 +1048,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) @@ -1055,9 +1075,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1), @@ -1088,9 +1109,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 0), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1), @@ -1116,8 +1138,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 0), @Expect(type = ActionCreatedEvent.class, count = 1), + @Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1), @@ -1145,9 +1169,10 @@ class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServiceIntegr @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class), @Expect(type = ActionCreatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/DeletedException.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/DeletedException.java new file mode 100644 index 000000000..3411b27df --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/DeletedException.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.hawkbit.repository.exception; + +import org.eclipse.hawkbit.exception.AbstractServerRtException; +import org.eclipse.hawkbit.exception.SpServerError; +import org.eclipse.hawkbit.repository.model.BaseEntity; + +import java.io.Serial; + +/** + * Thrown if assignment quota is exceeded + */ +public class DeletedException extends AbstractServerRtException { + + @Serial + private static final long serialVersionUID = 1L; + + private static final SpServerError THIS_ERROR = SpServerError.SP_DELETED; + + public DeletedException( + final Class type, final Object entityId) { + super(type.getSimpleName() + " with given identifier {" + entityId + "} is soft-deleted!", + THIS_ERROR); + } +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/LockedException.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/LockedException.java index 95c00ff76..7b3954a46 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/LockedException.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/LockedException.java @@ -16,20 +16,18 @@ import org.eclipse.hawkbit.repository.model.BaseEntity; import java.io.Serial; /** - * Thrown if assignment quota is exceeded + * Thrown if there is attempt to functionally modify a locked entity */ public class LockedException extends AbstractServerRtException { @Serial private static final long serialVersionUID = 1L; - private static final String ASSIGNMENT_QUOTA_EXCEEDED_MESSAGE = "Quota exceeded: Cannot assign %s more %s entities to %s '%s'. The maximum is %s."; private static final SpServerError THIS_ERROR = SpServerError.SP_LOCKED; public LockedException( final Class type, final Object entityId, final String operation) { - super( - type.getSimpleName() + " with given identifier {" + entityId + "} is locked and " + operation + + super(type.getSimpleName() + " with given identifier {" + entityId + "} is locked and " + operation + " is forbidden!", THIS_ERROR); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java index 4f9ce769e..f0997f34d 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDeploymentManagement.java @@ -329,10 +329,11 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl } private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final String initiatedBy, - final Long dsID, final Collection targetsWithActionType, final String actionMessage, + final Long dsId, final Collection targetsWithActionType, final String actionMessage, final AbstractDsAssignmentStrategy assignmentStrategy) { - final RetryCallback retryCallback = retryContext -> assignDistributionSetToTargets( - initiatedBy, dsID, targetsWithActionType, actionMessage, assignmentStrategy); + final RetryCallback retryCallback = + retryContext -> assignDistributionSetToTargets( + initiatedBy, dsId, targetsWithActionType, actionMessage, assignmentStrategy); return retryTemplate.execute(retryCallback); } @@ -351,7 +352,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl * * @param initiatedBy * the username of the user who initiated the assignment - * @param dsID + * @param dsId * the ID of the distribution set to assign * @param targetsWithActionType * a list of all targets and their action type @@ -365,12 +366,20 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl * if mandatory {@link SoftwareModuleType} are not assigned as * define by the {@link DistributionSetType}. */ - private DistributionSetAssignmentResult assignDistributionSetToTargets(final String initiatedBy, final Long dsID, + private DistributionSetAssignmentResult assignDistributionSetToTargets(final String initiatedBy, final Long dsId, final Collection targetsWithActionType, final String actionMessage, final AbstractDsAssignmentStrategy assignmentStrategy) { + final JpaDistributionSet distributionSet = + (JpaDistributionSet) distributionSetManagement.getValidAndComplete(dsId); + // implicit lock + if (!distributionSet.isLocked()) { + // without new transaction DS changed event is not thrown + DeploymentHelper.runInNewTransaction(txManager, "Implicit lock", status -> { + distributionSetManagement.lock(distributionSet.getId()); + return null; + }); + } - final JpaDistributionSet distributionSetEntity = (JpaDistributionSet) distributionSetManagement - .getValidAndComplete(dsID); final List providedTargetIds = targetsWithActionType.stream().map(TargetWithActionType::getControllerId) .distinct().toList(); @@ -381,19 +390,19 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl .flatMap(List::stream).map(JpaTarget::getControllerId).toList(); final List targetEntities = assignmentStrategy.findTargetsForAssignment(existingTargetIds, - distributionSetEntity.getId()); + distributionSet.getId()); if (targetEntities.isEmpty()) { - return allTargetsAlreadyAssignedResult(distributionSetEntity, existingTargetIds.size()); + return allTargetsAlreadyAssignedResult(distributionSet, existingTargetIds.size()); } final List existingTargetsWithActionType = targetsWithActionType.stream() .filter(target -> existingTargetIds.contains(target.getControllerId())).toList(); final List assignedActions = doAssignDistributionSetToTargets(initiatedBy, - existingTargetsWithActionType, actionMessage, assignmentStrategy, distributionSetEntity, + existingTargetsWithActionType, actionMessage, assignmentStrategy, distributionSet, targetEntities); - return buildAssignmentResult(distributionSetEntity, assignedActions, existingTargetsWithActionType.size()); + return buildAssignmentResult(distributionSet, assignedActions, existingTargetsWithActionType.size()); } private DistributionSetAssignmentResult allTargetsAlreadyAssignedResult( diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java index d0c71be1c..8be6a0ad6 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaDistributionSetInvalidationManagement.java @@ -20,6 +20,7 @@ import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.RepositoryProperties; import org.eclipse.hawkbit.repository.RolloutManagement; import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; +import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException; import org.eclipse.hawkbit.repository.exception.StopRolloutException; import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository; import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper; @@ -94,7 +95,6 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet // no lock is needed as no rollout will be stopped invalidateDistributionSetsInTransaction(distributionSetInvalidation, tenant); } - } private void invalidateDistributionSetsInTransaction(final DistributionSetInvalidation distributionSetInvalidation, @@ -108,21 +108,25 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet private void invalidateDistributionSet(final long setId, final CancelationType cancelationType, final boolean cancelRollouts) { - final DistributionSet set = distributionSetManagement.getValidAndComplete(setId); - distributionSetManagement.invalidate(set); + final DistributionSet distributionSet = distributionSetManagement.getOrElseThrowException(setId); + if (!distributionSet.isComplete()) { + throw new IncompleteDistributionSetException("Distribution set of type " + + distributionSet.getType().getKey() + " is incomplete: " + distributionSet.getId()); + } + distributionSetManagement.invalidate(distributionSet); log.debug("Distribution set {} marked as invalid.", setId); // rollout cancellation should only be permitted with UPDATE_ROLLOUT permission if (shouldRolloutsBeCanceled(cancelationType, cancelRollouts)) { log.debug("Cancel rollouts after ds invalidation. ID: {}", setId); - rolloutManagement.cancelRolloutsForDistributionSet(set); + rolloutManagement.cancelRolloutsForDistributionSet(distributionSet); } // Do run as system to ensure all actions (even invisible) are canceled due to invalidation. systemSecurityContext.runAsSystem(() -> { if (cancelationType != CancelationType.NONE) { log.debug("Cancel actions after ds invalidation. ID: {}", setId); - deploymentManagement.cancelActionsForDistributionSet(cancelationType, set); + deploymentManagement.cancelActionsForDistributionSet(cancelationType, distributionSet); } log.debug("Cancel auto assignments after ds invalidation. ID: {}", setId); 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 0fd26b1e0..3aa14e13b 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 @@ -34,6 +34,7 @@ import org.eclipse.hawkbit.repository.builder.DistributionSetCreate; import org.eclipse.hawkbit.repository.builder.DistributionSetUpdate; 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.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException; @@ -697,6 +698,13 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { public void lock(final long id) { final JpaDistributionSet distributionSet = getById(id); if (!distributionSet.isLocked()) { + distributionSet.getModules().forEach(module -> { + if (!module.isLocked()) { + final JpaSoftwareModule jpaSoftwareModule = (JpaSoftwareModule)module; + jpaSoftwareModule.lock(); + softwareModuleRepository.save(jpaSoftwareModule); + } + }); distributionSet.lock(); distributionSetRepository.save(distributionSet); } @@ -780,6 +788,10 @@ public class JpaDistributionSetManagement implements DistributionSetManagement { + distributionSet.getType().getKey() + " is incomplete: " + distributionSet.getId()); } + if (distributionSet.isDeleted()) { + throw new DeletedException(DistributionSet.class, id); + } + return distributionSet; } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaRolloutManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaRolloutManagement.java index 76d23176c..5d0b782ea 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaRolloutManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaRolloutManagement.java @@ -49,6 +49,7 @@ import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper; import org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate; import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; +import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaRollout; import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup; import org.eclipse.hawkbit.repository.jpa.model.JpaRollout_; @@ -205,22 +206,29 @@ public class JpaRolloutManagement implements RolloutManagement { private JpaRollout createRollout(final JpaRollout rollout) { WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(rollout); - long totalTargets; - String errMsg; + final JpaDistributionSet distributionSet = (JpaDistributionSet) rollout.getDistributionSet(); + final long totalTargets; + final String errMsg; if (RolloutHelper.isRolloutRetried(rollout.getTargetFilterQuery())) { totalTargets = targetManagement.countByFailedInRollout( RolloutHelper.getIdFromRetriedTargetFilter(rollout.getTargetFilterQuery()), - rollout.getDistributionSet().getType().getId()); + distributionSet.getType().getId()); errMsg = "No failed targets in Rollout"; } else { totalTargets = targetManagement.countByRsqlAndCompatible(rollout.getTargetFilterQuery(), - rollout.getDistributionSet().getType().getId()); + distributionSet.getType().getId()); errMsg = "Rollout does not match any existing targets"; } if (totalTargets == 0) { throw new ValidationException(errMsg); } rollout.setTotalTargets(totalTargets); + + // implicit lock + if (!distributionSet.isLocked()) { + distributionSetManagement.lock(distributionSet.getId()); + } + if (rollout.getWeight().isEmpty()) { rollout.setWeight(repositoryProperties.getActionWeightIfAbsent()); } @@ -526,7 +534,6 @@ public class JpaRolloutManagement implements RolloutManagement { rollout.setStartAt(update.getStartAt().orElse(null)); update.getSet().ifPresent(setId -> { final DistributionSet set = distributionSetManagement.getValidAndComplete(setId); - rollout.setDistributionSet(set); }); if (rolloutApprovalStrategy.isApprovalNeeded(rollout)) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetFilterQueryManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetFilterQueryManagement.java index 9be77bd90..e9348d101 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetFilterQueryManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/management/JpaTargetFilterQueryManagement.java @@ -276,11 +276,14 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme } else { WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(update); assertMaxTargetsQuota(targetFilterQuery.getQuery(), targetFilterQuery.getName(), update.getDsId()); - final JpaDistributionSet ds = (JpaDistributionSet) distributionSetManagement + final JpaDistributionSet distributionSet = (JpaDistributionSet) distributionSetManagement .getValidAndComplete(update.getDsId()); - verifyDistributionSetAndThrowExceptionIfDeleted(ds); + // implicit lock + if (!distributionSet.isLocked()) { + distributionSetManagement.lock(distributionSet.getId()); + } - targetFilterQuery.setAutoAssignDistributionSet(ds); + targetFilterQuery.setAutoAssignDistributionSet(distributionSet); contextAware.getCurrentContext().ifPresent(targetFilterQuery::setAccessControlContext); targetFilterQuery.setAutoAssignInitiatedBy(contextAware.getCurrentUsername()); targetFilterQuery.setAutoAssignActionType(sanitizeAutoAssignActionType(update.getActionType())); @@ -299,12 +302,6 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme .isConfirmationFlowEnabled(); } - private static void verifyDistributionSetAndThrowExceptionIfDeleted(final DistributionSet distributionSet) { - if (distributionSet.isDeleted()) { - throw new EntityNotFoundException(DistributionSet.class, distributionSet.getId()); - } - } - private static ActionType sanitizeAutoAssignActionType(final ActionType actionType) { if (actionType == null) { return ActionType.FORCED; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TestHelper.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TestHelper.java new file mode 100644 index 000000000..2f137a98b --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TestHelper.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2024 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.hawkbit.repository.jpa; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; +import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule; +import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.SoftwareModule; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class TestHelper { + + public static void implicitLock(final DistributionSet set) { + ((JpaDistributionSet) set).setOptLockRevision(set.getOptLockRevision() + 1); + } + + public static void implicitLock(final SoftwareModule module) { + ((JpaSoftwareModule) module).setOptLockRevision(module.getOptLockRevision() + 1); + } +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/controller/DistributionSetAccessControllerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/controller/DistributionSetAccessControllerTest.java index 5d5c6ca31..cbd4ef596 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/controller/DistributionSetAccessControllerTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/controller/DistributionSetAccessControllerTest.java @@ -264,12 +264,17 @@ class DistributionSetAccessControllerTest extends AbstractAccessControllerTest { @Test void verifyAutoAssignmentUsage() { // permit all operations first to prepare test setup + permitAllOperations(AccessController.Operation.READ); permitAllOperations(AccessController.Operation.CREATE); permitAllOperations(AccessController.Operation.UPDATE); final DistributionSet permitted = testdataFactory.createDistributionSet(); final DistributionSet readOnly = testdataFactory.createDistributionSet(); final DistributionSet hidden = testdataFactory.createDistributionSet(); + // has to lock them, otherwise implicit lock shall be made which require DistributionSet update permissions + distributionSetManagement.lock(permitted.getId()); + distributionSetManagement.lock(readOnly.getId()); + distributionSetManagement.lock(hidden.getId()); // entities created - reset rules testAccessControlManger.deleteAllRules(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/controller/TargetAccessControllerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/controller/TargetAccessControllerTest.java index c00eeb598..f8fb99482 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/controller/TargetAccessControllerTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/acm/controller/TargetAccessControllerTest.java @@ -205,6 +205,14 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest { @Test @Description("Verifies rules for target assignment") void verifyTargetAssignment() { + permitAllOperations(AccessController.Operation.READ); + permitAllOperations(AccessController.Operation.CREATE); + permitAllOperations(AccessController.Operation.UPDATE); + final DistributionSet ds = testdataFactory.createDistributionSet("myDs"); + distributionSetManagement.lock(ds.getId()); + // entities created - reset rules + testAccessControlManger.deleteAllRules(); + permitAllOperations(AccessController.Operation.READ); permitAllOperations(AccessController.Operation.CREATE); @@ -214,8 +222,6 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest { final Target hiddenTarget = targetManagement .create(entityFactory.target().create().controllerId("device02").status(TargetUpdateStatus.REGISTERED)); - final DistributionSet ds = testdataFactory.createDistributionSet("myDs"); - // define access controlling rule defineAccess(AccessController.Operation.READ, permittedTarget); @@ -246,6 +252,16 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest { @Test @Description("Verifies rules for target assignment") void verifyTargetAssignmentOnNonUpdatableTarget() { + permitAllOperations(AccessController.Operation.READ); + permitAllOperations(AccessController.Operation.CREATE); + permitAllOperations(AccessController.Operation.UPDATE); + final DistributionSet firstDs = testdataFactory.createDistributionSet("myDs"); + distributionSetManagement.lock(firstDs.getId()); + final DistributionSet secondDs = testdataFactory.createDistributionSet("anotherDs"); + distributionSetManagement.lock(secondDs.getId()); + // entities created - reset rules + testAccessControlManger.deleteAllRules(); + permitAllOperations(AccessController.Operation.READ); permitAllOperations(AccessController.Operation.CREATE); @@ -255,8 +271,6 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest { final Target readOnlyTarget = targetManagement .create(entityFactory.target().create().controllerId("device02").status(TargetUpdateStatus.REGISTERED)); - final DistributionSet firstDs = testdataFactory.createDistributionSet("myDs"); - // define access controlling rule defineAccess(AccessController.Operation.READ, manageableTarget, readOnlyTarget); @@ -271,8 +285,6 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest { () -> assignDistributionSet(firstDs.getId(), readOnlyTarget.getControllerId()) ).isInstanceOf(AssertionError.class); - final DistributionSet secondDs = testdataFactory.createDistributionSet("anotherDs"); - // bunch assignment skips denied denied since at least one target without update // permissions is present assertThat(assignDistributionSet(secondDs.getId(), @@ -283,19 +295,27 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest { @Test @Description("Verifies only manageable targets are part of the rollout") void verifyRolloutTargetScope() { + permitAllOperations(AccessController.Operation.READ); + permitAllOperations(AccessController.Operation.CREATE); + permitAllOperations(AccessController.Operation.UPDATE); + final DistributionSet ds = testdataFactory.createDistributionSet("myDs"); + distributionSetManagement.lock(ds.getId()); + // entities created - reset rules + testAccessControlManger.deleteAllRules(); + permitAllOperations(AccessController.Operation.READ); permitAllOperations(AccessController.Operation.CREATE); final List updateTargets = testdataFactory.createTargets("update1", "update2", "update3"); final List readTargets = testdataFactory.createTargets("read1", "read2", "read3", "read4"); - final List hiddenTargets = testdataFactory.createTargets("hidden1", "hidden2", "hidden3", "hidden4", - "hidden5"); + final List hiddenTargets = testdataFactory.createTargets( + "hidden1", "hidden2", "hidden3", "hidden4", "hidden5"); defineAccess(AccessController.Operation.UPDATE, updateTargets); defineAccess(AccessController.Operation.READ, merge(readTargets, updateTargets)); final Rollout rollout = testdataFactory.createRolloutByVariables("testRollout", "description", - updateTargets.size(), "id==*", testdataFactory.createDistributionSet(), "50", "5"); + updateTargets.size(), "id==*", ds, "50", "5"); assertThat(rollout.getTotalTargets()).isEqualTo(updateTargets.size()); @@ -318,6 +338,14 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest { @Test @Description("Verifies only manageable targets are part of an auto assignment.") void verifyAutoAssignmentTargetScope() { + permitAllOperations(AccessController.Operation.READ); + permitAllOperations(AccessController.Operation.CREATE); + permitAllOperations(AccessController.Operation.UPDATE); + final DistributionSet distributionSet = testdataFactory.createDistributionSet(); + distributionSetManagement.lock(distributionSet.getId()); + // entities created - reset rules + testAccessControlManger.deleteAllRules(); + permitAllOperations(AccessController.Operation.CREATE); final List updateTargets = testdataFactory.createTargets("update1", "update2", "update3"); @@ -331,7 +359,6 @@ class TargetAccessControllerTest extends AbstractAccessControllerTest { final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement .create(entityFactory.targetFilterQuery().create().name("testName").query("id==*")); - final DistributionSet distributionSet = testdataFactory.createDistributionSet(); testAccessControlManger.defineAccessRule( JpaDistributionSet.class, AccessController.Operation.READ, DistributionSetSpecification.byId(distributionSet.getId()), diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerIntTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerIntTest.java index 72fe63161..bd5af4b43 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerIntTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerIntTest.java @@ -23,6 +23,7 @@ import java.util.stream.Stream; import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException; import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; +import org.eclipse.hawkbit.repository.jpa.TestHelper; import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.ActionType; @@ -108,11 +109,8 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest { @Test @Description("Test auto assignment of a DS to filtered targets") void checkAutoAssign() { - - final DistributionSet setA = testdataFactory.createDistributionSet("dsA"); // will - // be - // auto - // assigned + // will be auto assigned + final DistributionSet setA = testdataFactory.createDistributionSet("dsA"); final DistributionSet setB = testdataFactory.createDistributionSet("dsB"); // target filter query that matches all targets @@ -121,6 +119,7 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest { .updateAutoAssign(targetFilterQueryManagement .create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*")).getId()) .ds(setA.getId())); + TestHelper.implicitLock(setA); final String targetDsAIdPref = "targ"; final List targets = testdataFactory.createTargets(25, targetDsAIdPref, @@ -129,12 +128,15 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest { // assign set A to first 10 targets assignDistributionSet(setA, targets.subList(0, 10)); + // because of targetFilterQuery the assigned to targets group is a locked one + // in the rest it is locked in process and targets gets unlocked (?) verifyThatTargetsHaveDistributionSetAssignment(setA, targets.subList(0, 10), targetsCount); // assign set B to first 5 targets // they have now 2 DS in their action history and should not get updated // with dsA assignDistributionSet(setB, targets.subList(0, 5)); + TestHelper.implicitLock(setB); verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount); // assign set B to next 10 targets @@ -167,6 +169,7 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest { .updateAutoAssign(targetFilterQueryManagement .create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*")).getId()) .ds(toAssignDs.getId())); + TestHelper.implicitLock(toAssignDs); final List targets = testdataFactory.createTargets(25); final int targetsCount = targets.size(); @@ -267,6 +270,7 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest { // target filter query that matches failed bunch of targets targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("filterB") .query("id==" + targetDsAIdPref + "*").autoAssignDistributionSet(setA.getId())); + TestHelper.implicitLock(setA); final List targetsF = testdataFactory.createTargets(10, targetDsFIdPref, targetDsFIdPref.concat(" description")); @@ -278,6 +282,7 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest { // assign set B to first 5 targets of fail group assignDistributionSet(setB, targetsF.subList(0, 5)); + TestHelper.implicitLock(setB); verifyThatTargetsHaveDistributionSetAssignment(setB, targetsF.subList(0, 5), targetsCount); // Run the check @@ -311,7 +316,6 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest { .as("assigned DS").isEqualTo(set); } } - } @Step @@ -367,6 +371,7 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest { final List targetsA = createTargetsAndAutoAssignDistSet(targetDsAIdPref, 5, distributionSet, ActionType.FORCED); + TestHelper.implicitLock(distributionSet); final List targetsB = createTargetsAndAutoAssignDistSet(targetDsBIdPref, 10, distributionSet, ActionType.SOFT); final List targetsC = createTargetsAndAutoAssignDistSet(targetDsCIdPref, 10, distributionSet, @@ -498,4 +503,4 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest { .findAll(ActionSpecifications.byDistributionSetId(distributionSetId), pageable) .map(Action.class::cast); } -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ControllerManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ControllerManagementTest.java index 8e8ab2881..cbcf2874b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ControllerManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/ControllerManagementTest.java @@ -49,6 +49,7 @@ import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; @@ -160,11 +161,13 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Controller confirms successful update with FINISHED status.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = TargetAttributesRequestedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAttributesRequestedEvent.class, count = 1) }) void controllerConfirmsUpdateWithFinished() { final Long actionId = createTargetAndAssignDs(); @@ -183,9 +186,11 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Controller confirmation fails with invalid messages.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) void controllerConfirmationFailsWithInvalidMessages() { final Long actionId = createTargetAndAssignDs(); @@ -211,12 +216,14 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { + "Reason: The decision to ignore the cancellation is in fact up to the controller.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = CancelTargetAssignmentEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = TargetAttributesRequestedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAttributesRequestedEvent.class, count = 1) }) void controllerConfirmsUpdateWithFinishedAndIgnoresCancellationWithThat() { final Long actionId = createTargetAndAssignDs(); deploymentManagement.cancelAction(actionId); @@ -234,9 +241,11 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Update server rejects cancellation feedback if action is not in CANCELING state.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) void cancellationFeedbackRejectedIfActionIsNotInCanceling() { final Long actionId = createTargetAndAssignDs(); @@ -256,11 +265,13 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Controller confirms action cancellation with FINISHED status.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = CancelTargetAssignmentEvent.class, count = 1), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) void controllerConfirmsActionCancellationWithFinished() { final Long actionId = createTargetAndAssignDs(); @@ -283,11 +294,13 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Controller confirms action cancellation with FINISHED status.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = CancelTargetAssignmentEvent.class, count = 1), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) void controllerConfirmsActionCancellationWithCanceled() { final Long actionId = createTargetAndAssignDs(); @@ -311,11 +324,13 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { + "that the controller will continue the original update.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = CancelTargetAssignmentEvent.class, count = 1), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) void controllerRejectsActionCancellationWithReject() { final Long actionId = createTargetAndAssignDs(); @@ -339,11 +354,13 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { + "that the controller will continue the original update.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = CancelTargetAssignmentEvent.class, count = 1), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) void controllerRejectsActionCancellationWithError() { final Long actionId = createTargetAndAssignDs(); @@ -478,10 +495,11 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { + "is actually used for the check as long as they have an identical binary, i.e. same SHA1 hash. ") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 2), - @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 2) }) + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 5), // implicit lock + @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1), + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) void hasTargetArtifactAssignedIsTrueWithMultipleArtifacts() { final int artifactSize = 5 * 1024; final byte[] random = RandomUtils.nextBytes(artifactSize); @@ -810,10 +828,12 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Controller tries to finish an update process after it has been finished by an error action status.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) void tryToFinishWithErrorUpdateProcessMoreThanOnce() { final Long actionId = createTargetAndAssignDs(); @@ -856,11 +876,13 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Controller tries to finish an update process after it has been finished by an FINISHED action status.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = TargetAttributesRequestedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAttributesRequestedEvent.class, count = 1) }) void tryToFinishUpdateProcessMoreThanOnce() { final Long actionId = prepareFinishedUpdate().getId(); @@ -893,11 +915,13 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { + "configured to reject that.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = TargetAttributesRequestedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAttributesRequestedEvent.class, count = 1) }) void sendUpdatesForFinishUpdateProcessDroppedIfDisabled() { repositoryProperties.setRejectActionStatusForClosedAction(true); @@ -922,12 +946,14 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { + "configured to accept them.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = TargetAttributesRequestedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAttributesRequestedEvent.class, count = 1) }) void sendUpdatesForFinishUpdateProcessAcceptedIfEnabled() { repositoryProperties.setRejectActionStatusForClosedAction(false); @@ -1176,9 +1202,11 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Controller providing status entries fails if providing more than permitted by quota.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) public void controllerProvidesIntermediateFeedbackFailsIfQuotaHit() { final int allowStatusEntries = 10; final Long actionId = createTargetAndAssignDs(); @@ -1229,9 +1257,11 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Verifies that the quota specifying the maximum number of status entries per action is enforced.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 2), @Expect(type = DistributionSetCreatedEvent.class, count = 2), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 2), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 6) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 2) }) public void addActionStatusUpdatesUntilQuotaIsExceeded() { // any distribution set assignment causes 1 status entity to be created @@ -1265,9 +1295,11 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Verifies that the quota specifying the maximum number of messages per action status is enforced.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) public void createActionStatusWithTooManyMessages() { final int maxMessages = quotaManagement.getMaxMessagesPerActionStatus(); @@ -1293,9 +1325,11 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Verifies that a DOWNLOAD_ONLY action is not marked complete when the controller reports DOWNLOAD") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) public void controllerReportsDownloadForDownloadOnlyAction() { testdataFactory.createTarget(); final Long actionId = createAndAssignDsAsDownloadOnly("downloadOnlyDs", DEFAULT_CONTROLLER_ID); @@ -1314,11 +1348,13 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Verifies that a DOWNLOAD_ONLY action is marked complete once the controller reports DOWNLOADED") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetAttributesRequestedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) void controllerReportsDownloadedForDownloadOnlyAction() { testdataFactory.createTarget(); final Long actionId = createAndAssignDsAsDownloadOnly("downloadOnlyDs", DEFAULT_CONTROLLER_ID); @@ -1337,11 +1373,13 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Verifies that a controller can report a FINISHED event for a DOWNLOAD_ONLY non-active action.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 3), @Expect(type = TargetAttributesRequestedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 2), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) void controllerReportsActionFinishedForDownloadOnlyActionThatIsNotActive() { testdataFactory.createTarget(); final Long actionId = createAndAssignDsAsDownloadOnly("downloadOnlyDs", DEFAULT_CONTROLLER_ID); @@ -1360,11 +1398,14 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Verifies that multiple DOWNLOADED events for a DOWNLOAD_ONLY action are handled.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), - @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock + @Expect(type = ActionCreatedEvent.class, count = 1), + @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetAttributesRequestedEvent.class, count = 3), @Expect(type = ActionUpdatedEvent.class, count = 1), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) void controllerReportsMultipleDownloadedForDownloadOnlyAction() { testdataFactory.createTarget(); final Long actionId = createAndAssignDsAsDownloadOnly("downloadOnlyDs", DEFAULT_CONTROLLER_ID); @@ -1385,11 +1426,13 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { + "DOWNLOAD_ONLY action.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = TargetAttributesRequestedEvent.class, count = 9), - @Expect(type = ActionUpdatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = ActionUpdatedEvent.class, count = 1) }) void quotaExceptionWhencontrollerReportsTooManyDownloadedMessagesForDownloadOnlyAction() { final int maxMessages = quotaManagement.getMaxMessagesPerActionStatus(); testdataFactory.createTarget(); @@ -1405,11 +1448,13 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Verifies that quota is enforced for UpdateActionStatus events for DOWNLOAD_ONLY assignments.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetAttributesRequestedEvent.class, count = 9), @Expect(type = ActionUpdatedEvent.class, count = 1), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) void quotaExceededExceptionWhenControllerReportsTooManyUpdateActionStatusMessagesForDownloadOnlyAction() { final int maxMessages = quotaManagement.getMaxMessagesPerActionStatus(); testdataFactory.createTarget(); @@ -1436,9 +1481,11 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Description("Verifies that quota is enforced for UpdateActionStatus events for FORCED assignments.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) void quotaExceededExceptionWhenControllerReportsTooManyUpdateActionStatusMessagesForForced() { final int maxMessages = quotaManagement.getMaxMessagesPerActionStatus(); final Long actionId = createTargetAndAssignDs(); @@ -1527,11 +1574,13 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { + "repositoryProperties.rejectActionStatusForClosedAction value.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 4), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 12), + @Expect(type = DistributionSetUpdatedEvent.class, count = 4), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 12), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 4), @Expect(type = TargetUpdatedEvent.class, count = 12), @Expect(type = TargetAssignDistributionSetEvent.class, count = 4), @Expect(type = TargetAttributesRequestedEvent.class, count = 6), - @Expect(type = ActionUpdatedEvent.class, count = 8), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 12) }) + @Expect(type = ActionUpdatedEvent.class, count = 8) }) void targetCanAlwaysReportFinishedOrErrorAfterActionIsClosedForDownloadOnlyAssignments() { testdataFactory.createTarget(); @@ -1578,13 +1627,14 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { + " installed an intermediate update.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 2), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 5), @Expect(type = TargetAttributesRequestedEvent.class, count = 3), @Expect(type = ActionUpdatedEvent.class, count = 3), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 2), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 6) }) + @Expect(type = TargetAssignDistributionSetEvent.class, count = 2) }) void controllerReportsFinishedForOldDownloadOnlyActionAfterSuccessfulForcedAssignment() { - testdataFactory.createTarget(); final DistributionSet downloadOnlyDs = testdataFactory.createDistributionSet("downloadOnlyDs1"); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DeploymentManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DeploymentManagementTest.java index c72065ef2..352b5b4cf 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DeploymentManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DeploymentManagementTest.java @@ -42,6 +42,7 @@ import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TenantConfigurationCreatedEvent; @@ -54,6 +55,7 @@ import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetExcepti import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException; import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException; import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; +import org.eclipse.hawkbit.repository.jpa.TestHelper; import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus; import org.eclipse.hawkbit.repository.jpa.model.JpaAction_; @@ -279,7 +281,9 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = CancelTargetAssignmentEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 20), @Expect(type = DistributionSetCreatedEvent.class, count = 2), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 6) }) + @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6) }) // implicit lock }) void multiAssigmentHistoryOverMultiplePagesResultsInTwoActiveAction() { final DistributionSet cancelDs = testdataFactory.createDistributionSet("Canceled DS", "1.0", @@ -452,6 +456,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { private JpaAction assignSet(final Target target, final DistributionSet ds) { assignDistributionSet(ds.getId(), target.getControllerId()); + TestHelper.implicitLock(ds); assertThat(targetManagement.getByControllerID(target.getControllerId()).get().getUpdateStatus()) .as("wrong update status").isEqualTo(TargetUpdateStatus.PENDING); assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId())).as("wrong assigned ds") @@ -474,6 +479,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = TargetUpdatedEvent.class, count = 20), @Expect(type = ActionCreatedEvent.class, count = 20), @Expect(type = DistributionSetCreatedEvent.class, count = 2), @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 1) }) void assignedDistributionSet() { @@ -516,6 +523,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = TargetUpdatedEvent.class, count = 4), @Expect(type = ActionCreatedEvent.class, count = 4), @Expect(type = DistributionSetCreatedEvent.class, count = 4), @Expect(type = SoftwareModuleCreatedEvent.class, count = 12), + @Expect(type = DistributionSetUpdatedEvent.class, count = 4), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 12), // implicit lock @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) void multiOfflineAssignment() { final List targetIds = testdataFactory.createTargets(1).stream().map(Target::getControllerId) @@ -547,6 +556,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = ActionUpdatedEvent.class, count = 10), @Expect(type = DistributionSetCreatedEvent.class, count = 2), @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 2), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1), @Expect(type = TenantConfigurationUpdatedEvent.class, count = 1) }) @@ -585,6 +596,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = TargetUpdatedEvent.class, count = 20), @Expect(type = ActionCreatedEvent.class, count = 20), @Expect(type = DistributionSetCreatedEvent.class, count = 2), @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), // implicit lock @Expect(type = MultiActionAssignEvent.class, count = 2), @Expect(type = MultiActionCancelEvent.class, count = 0), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @@ -628,6 +641,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = TargetUpdatedEvent.class, count = 4), @Expect(type = ActionCreatedEvent.class, count = 4), @Expect(type = DistributionSetCreatedEvent.class, count = 4), @Expect(type = SoftwareModuleCreatedEvent.class, count = 12), + @Expect(type = DistributionSetUpdatedEvent.class, count = 4), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 12), // implicit lock @Expect(type = MultiActionAssignEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) @@ -658,6 +673,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = TargetUpdatedEvent.class, count = 4), @Expect(type = ActionCreatedEvent.class, count = 4), @Expect(type = DistributionSetCreatedEvent.class, count = 4), @Expect(type = SoftwareModuleCreatedEvent.class, count = 12), + @Expect(type = DistributionSetUpdatedEvent.class, count = 4), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 12), // implicit lock @Expect(type = MultiActionAssignEvent.class, count = 1), @Expect(type = MultiActionCancelEvent.class, count = 4), @Expect(type = ActionUpdatedEvent.class, count = 4), @@ -900,6 +917,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = MultiActionAssignEvent.class, count = 1), @@ -985,6 +1004,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = MultiActionAssignEvent.class, count = 2), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) @@ -1022,9 +1043,11 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Description("Simple deployment or distribution set to target assignment test.") @ExpectEvents({ @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetCreatedEvent.class, count = 30), @Expect(type = ActionCreatedEvent.class, count = 20), - @Expect(type = TargetUpdatedEvent.class, count = 20), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetUpdatedEvent.class, count = 20) }) void assignDistributionSet2Targets() { final String myCtrlIDPref = "myCtrlID"; @@ -1035,8 +1058,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { "first description"); final DistributionSet ds = testdataFactory.createDistributionSet(""); - assignDistributionSet(ds, savedDeployedTargets); + TestHelper.implicitLock(ds); // verify that one Action for each assignDistributionSet final Page actions = actionRepository.findAll(PAGE); @@ -1079,12 +1102,11 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Description("Test that it is not possible to assign a distribution set that is not complete.") @ExpectEvents({ @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), - @Expect(type = TargetCreatedEvent.class, count = 10), @Expect(type = ActionCreatedEvent.class, count = 10), - @Expect(type = TargetUpdatedEvent.class, count = 10), @Expect(type = SoftwareModuleCreatedEvent.class, count = 2), - @Expect(type = DistributionSetUpdatedEvent.class, count = 1) - - }) + @Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = TargetCreatedEvent.class, count = 10), @Expect(type = ActionCreatedEvent.class, count = 10), + @Expect(type = TargetUpdatedEvent.class, count = 10) }) void failDistributionSetAssigmentThatIsNotComplete() throws InterruptedException { final List targets = testdataFactory.createTargets(10); @@ -1115,6 +1137,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = CancelTargetAssignmentEvent.class, count = 2), @Expect(type = DistributionSetCreatedEvent.class, count = 3), @Expect(type = SoftwareModuleCreatedEvent.class, count = 9), + @Expect(type = DistributionSetUpdatedEvent.class, count = 3), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 9), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 3) }) void multipleDeployments() { final String undeployedTargetPrefix = "undep-T"; @@ -1219,8 +1243,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { // verify, that dsA is deployed correctly for (final Target t_ : updatedTsDsA) { final Target t = targetManagement.getByControllerID(t_.getControllerId()).get(); - assertThat(deploymentManagement.getAssignedDistributionSet(t.getControllerId())).as("assigned ds is wrong") - .contains(dsA); + assertThat(deploymentManagement.getAssignedDistributionSet(t.getControllerId())) + .as("assigned ds is wrong").contains(dsA); assertThat(deploymentManagement.getInstalledDistributionSet(t.getControllerId())) .as("installed ds is wrong").contains(dsA); assertThat(targetManagement.getByControllerID(t.getControllerId()).get().getUpdateStatus()) @@ -1361,11 +1385,12 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { // doing the assignment targs = assignDistributionSet(dsA, targs).getAssignedEntity().stream().map(Action::getTarget) .collect(Collectors.toList()); + TestHelper.implicitLock(dsA); Target targ = targetManagement.getByControllerID(targs.iterator().next().getControllerId()).get(); // checking the revisions of the created entities // verifying that the revision of the object and the revision within the - // DB has not changed + // DB has incremented by implicit lock assertThat(dsA.getOptLockRevision()).as("lock revision is wrong") .isEqualTo(distributionSetManagement.getWithDetails(dsA.getId()).get().getOptLockRevision()); @@ -1402,6 +1427,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { targs = assignDistributionSet(dsB.getId(), "target-id-A").getAssignedEntity().stream().map(Action::getTarget) .collect(Collectors.toList()); + TestHelper.implicitLock(dsB); targ = targs.iterator().next(); @@ -1417,7 +1443,6 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { "Installed ds is wrong"); assertEquals(dsB, deploymentManagement.findActiveActionsByTarget(PAGE, targ.getControllerId()).getContent() .get(0).getDistributionSet(), "Active ds is wrong"); - } @Test @@ -1433,7 +1458,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { assignDistributionSet(dsA, Collections.singletonList(targ)); - assertThat(dsA.getOptLockRevision()).as("lock revision is wrong") + // implicit lock - incremented the version + assertThat(dsA.getOptLockRevision() + 1).as("lock revision is wrong") .isEqualTo(distributionSetManagement.getWithDetails(dsA.getId()).get().getOptLockRevision()); } @@ -1568,6 +1594,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { } deploymentManagement.assignDistributionSets(deploymentRequests); + TestHelper.implicitLock(ds); final List content = targetManagement.findAll(Pageable.unpaged()).getContent(); @@ -1593,6 +1620,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { final List deploymentRequests = Arrays.asList(deployment1, deployment2); deploymentManagement.assignDistributionSets(deploymentRequests); + TestHelper.implicitLock(ds); final Optional assignedDsTarget1 = targetManagement .getByControllerID(target1.getControllerId()).map(JpaTarget.class::cast) @@ -1680,11 +1708,11 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { for (final DistributionSet ds : dsList) { deployedTargets = assignDistributionSet(ds, deployedTargets).getAssignedEntity().stream() .map(Action::getTarget).collect(Collectors.toList()); + TestHelper.implicitLock(ds); } return new DeploymentResult(deployedTargets, nakedTargets, dsList, deployedTargetPrefix, undeployedTargetPrefix, distributionSetPrefix); - } private Slice findActionsByDistributionSet(final Pageable pageable, final long distributionSetId) { @@ -1744,4 +1772,4 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { return undeployedTargetIDs; } } -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetInvalidationManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetInvalidationManagementTest.java index 147481e70..ed8380c3c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetInvalidationManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/DistributionSetInvalidationManagementTest.java @@ -191,15 +191,13 @@ class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTe } @Test - @Description("Verify that invalidating an invalidated distribution set throws an exception") - void verifyInvalidateInvalidatedDistributionSetThrowsException() { + @Description("Verify that invalidating an invalidated distribution set don't throws an exception" + + " -> should be able to cancel actions again (if previous time there was a problem") + void verifyInvalidateInvalidatedDistributionSetDontThrowsException() { final DistributionSet distributionSet = testdataFactory.createAndInvalidateDistributionSet(); - - assertThatExceptionOfType(InvalidDistributionSetException.class) - .as("Invalid distributionSet should throw an exception") - .isThrownBy(() -> distributionSetInvalidationManagement.invalidateDistributionSet( + distributionSetInvalidationManagement.invalidateDistributionSet( new DistributionSetInvalidation(Collections.singletonList(distributionSet.getId()), - CancelationType.SOFT, true))); + CancelationType.SOFT, true)); } @Test 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 3c73bf172..cd333de82 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 @@ -18,7 +18,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; @@ -46,6 +45,7 @@ import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException; import org.eclipse.hawkbit.repository.exception.LockedException; import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException; import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; +import org.eclipse.hawkbit.repository.jpa.TestHelper; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata; import org.eclipse.hawkbit.repository.model.Action.Status; @@ -626,7 +626,6 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Tests that a DS queue is possible where the result is ordered by the target assignment, i.e. assigned first in the list.") void findDistributionSetsAllOrderedByLinkTarget() { - final List buildDistributionSets = testdataFactory.createDistributionSets("dsOrder", 10); final List buildTargetFixtures = testdataFactory.createTargets(5, "tOrder", "someDesc"); @@ -642,12 +641,15 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest { // set assigned assignDistributionSet(dsSecond.getId(), tSecond.getControllerId()); + TestHelper.implicitLock(dsSecond); assignDistributionSet(dsThree.getId(), tFirst.getControllerId()); + TestHelper.implicitLock(dsThree); // set installed testdataFactory.sendUpdateActionStatusToTargets(Collections.singleton(tSecond), Status.FINISHED, singletonList("some message")); assignDistributionSet(dsFour.getId(), tSecond.getControllerId()); + TestHelper.implicitLock(dsFour); final DistributionSetFilter distributionSetFilter = DistributionSetFilter.builder() @@ -702,7 +704,6 @@ class DistributionSetManagementTest extends AbstractJpaIntegrationTest { assertThat(tSecondPinOrderedByName.get(7)).isEqualTo(buildDistributionSets.get(4)); assertThat(tSecondPinOrderedByName.get(8)).isEqualTo(buildDistributionSets.get(2)); assertThat(tSecondPinOrderedByName.get(9)).isEqualTo(buildDistributionSets.get(0)); - } @Test diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutGroupManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutGroupManagementTest.java index 8e1d681ea..f6f933d30 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutGroupManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutGroupManagementTest.java @@ -15,11 +15,13 @@ import java.util.List; import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.RolloutCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; import org.eclipse.hawkbit.repository.model.Action; @@ -62,6 +64,8 @@ class RolloutGroupManagementTest extends AbstractJpaIntegrationTest { @Expect(type = RolloutGroupUpdatedEvent.class, count = 5), @Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = RolloutUpdatedEvent.class, count = 1), @Expect(type = TargetCreatedEvent.class, count = 125), @Expect(type = RolloutCreatedEvent.class, count = 1) }) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutManagementTest.java index fc90b7954..84e88d5ee 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/RolloutManagementTest.java @@ -44,11 +44,13 @@ import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEv import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.RolloutCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; @@ -243,6 +245,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { @Expect(type = RolloutGroupUpdatedEvent.class, count = 5), @Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = RolloutCreatedEvent.class, count = 1), @Expect(type = RolloutUpdatedEvent.class, count = 1), @Expect(type = TargetCreatedEvent.class, count = 125) }) void entityQueriesReferringToNotExistingEntitiesThrowsException() { @@ -1833,11 +1837,13 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { @Test @ExpectEvents({ @Expect(type = RolloutDeletedEvent.class, count = 1), - @Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = TargetCreatedEvent.class, count = 25), @Expect(type = RolloutUpdatedEvent.class, count = 2), @Expect(type = RolloutGroupCreatedEvent.class, count = 5), @Expect(type = RolloutGroupDeletedEvent.class, count = 5), + @Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = RolloutGroupUpdatedEvent.class, count = 5), @Expect(type = RolloutCreatedEvent.class, count = 1) }) void deleteRolloutWhichHasNeverStartedIsHardDeleted() { @@ -1866,6 +1872,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { @Expect(type = RolloutGroupUpdatedEvent.class, count = 10), @Expect(type = RolloutUpdatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetCreatedEvent.class, count = 25), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = RolloutGroupCreatedEvent.class, count = 5), 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 7ce9a78d1..27d1159a9 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 @@ -31,6 +31,7 @@ import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.LockedException; import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; import org.eclipse.hawkbit.repository.jpa.RandomGeneratedInputStream; +import org.eclipse.hawkbit.repository.jpa.TestHelper; import org.eclipse.hawkbit.repository.jpa.model.JpaAction_; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_; @@ -196,6 +197,8 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { final JpaTarget target = (JpaTarget) testdataFactory.createTarget(); ds = (JpaDistributionSet) assignSet(target, ds).getDistributionSet(); + TestHelper.implicitLock(os); + TestHelper.implicitLock(jvm); // standard searches assertThat(softwareModuleManagement.findByTextAndType(PAGE, "poky", osType.getId()).getContent()).hasSize(1); @@ -203,7 +206,8 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { .isEqualTo(os); assertThat(softwareModuleManagement.findByTextAndType(PAGE, "oracle", runtimeType.getId()).getContent()) .hasSize(1); - assertThat(softwareModuleManagement.findByTextAndType(PAGE, "oracle", runtimeType.getId()).getContent().get(0)) + assertThat( + softwareModuleManagement.findByTextAndType(PAGE, "oracle", runtimeType.getId()).getContent().get(0)) .isEqualTo(jvm); assertThat(softwareModuleManagement.findByTextAndType(PAGE, ":1.0.1", appType.getId()).getContent()).hasSize(1) .first().isEqualTo(ah); @@ -218,6 +222,7 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { private Action assignSet(final JpaTarget target, final JpaDistributionSet ds) { assignDistributionSet(ds.getId(), target.getControllerId()); + TestHelper.implicitLock(ds); assertThat(targetManagement.getByControllerID(target.getControllerId()).get().getUpdateStatus()) .isEqualTo(TargetUpdateStatus.PENDING); final Optional assignedDistributionSet = deploymentManagement @@ -864,12 +869,14 @@ public class SoftwareModuleManagementTest extends AbstractJpaIntegrationTest { final long artifactId = softwareModuleManagement.get(softwareModule.getId()).get() .getArtifacts().stream().findFirst().get().getId(); assertThatExceptionOfType(LockedException.class) - .as("Attempt to modify a locked DS software modules should throw an exception") + .as("Attempt to modify a locked SM artifacts should throw an exception") .isThrownBy(() -> artifactManagement.delete(artifactId)); assertThat(softwareModuleManagement.get(softwareModule.getId()).get().getArtifacts().size()) - .as("Software module shall not be removed from a locked DS.") + .as("Artifact shall not be removed from a locked SM.") .isEqualTo(artifactCount); - assertThat(artifactManagement.get(artifactId)).isPresent(); + assertThat(artifactManagement.get(artifactId)) + .as("Artifact shall not be removed if belongs to a locked SM.") + .isPresent(); } @Test diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetFilterQueryManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetFilterQueryManagementTest.java index c30b89f22..4570a07cc 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetFilterQueryManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetFilterQueryManagementTest.java @@ -30,14 +30,14 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedE import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetFilterQueryCreatedEvent; import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; +import org.eclipse.hawkbit.repository.exception.DeletedException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; -import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException; import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException; import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException; -import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException; import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException; import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; +import org.eclipse.hawkbit.repository.jpa.TestHelper; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.DistributionSet; @@ -220,6 +220,7 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest final TargetFilterQuery targetFilterQuery, final DistributionSet distributionSet) { targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery() .updateAutoAssign(targetFilterQuery.getId()).ds(distributionSet.getId())); + TestHelper.implicitLock(distributionSet); verifyAutoAssignDsAndActionType(filterName, distributionSet, ActionType.FORCED); } @@ -237,7 +238,6 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest targetFilterQueryManagement .updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(targetFilterQuery.getId()) .ds(distributionSet.getId()).actionType(ActionType.DOWNLOAD_ONLY)); - verifyAutoAssignDsAndActionType(filterName, distributionSet, ActionType.DOWNLOAD_ONLY); } @@ -269,19 +269,11 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest assignDistributionSet(softDeletedDs, testdataFactory.createTarget("forSoftDeletedDs")); distributionSetManagement.delete(softDeletedDs.getId()); - assertThatExceptionOfType(EntityNotFoundException.class) + assertThatExceptionOfType(DeletedException.class) .isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery() .updateAutoAssign(targetFilterQuery.getId()).ds(softDeletedDs.getId()))); } - private void verifyAutoAssignDsAndActionType(final String filterName, final DistributionSet distributionSet, - final ActionType actionType) { - final TargetFilterQuery tfq = targetFilterQueryManagement.getByName(filterName).get(); - - assertEquals(distributionSet, tfq.getAutoAssignDistributionSet(), "Returns correct distribution set"); - assertEquals(actionType, tfq.getAutoAssignActionType(), "Return correct action type"); - } - @Test @Description("Assigns a distribution set to an existing filter query and verifies that the quota 'max targets per auto assignment' is enforced.") public void assignDistributionSetToTargetFilterQueryThatExceedsQuota() { @@ -332,6 +324,7 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery() .updateAutoAssign(targetFilterQuery.getId()).ds(distributionSet.getId())); + TestHelper.implicitLock(distributionSet); // Check if target filter query is there TargetFilterQuery tfq = targetFilterQueryManagement.getByName(filterName).get(); @@ -363,6 +356,7 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest .getId(); targetFilterQueryManagement.updateAutoAssignDS( entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(distributionSet.getId())); + TestHelper.implicitLock(distributionSet); // Check if target filter query is there with the distribution set TargetFilterQuery tfq = targetFilterQueryManagement.getByName(filterName).get(); @@ -569,4 +563,12 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest .updateAutoAssignDS(new AutoAssignDistributionSetUpdate(targetFilterQuery.getId()) .ds(incompleteDistributionSet.getId()))); } -} + + private void verifyAutoAssignDsAndActionType(final String filterName, final DistributionSet distributionSet, + final ActionType actionType) { + final TargetFilterQuery tfq = targetFilterQueryManagement.getByName(filterName).get(); + + assertEquals(distributionSet, tfq.getAutoAssignDistributionSet(), "Returns correct distribution set"); + assertEquals(actionType, tfq.getAutoAssignActionType(), "Return correct action type"); + } +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java index 432e427c5..e26621308 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/management/TargetManagementTest.java @@ -40,7 +40,9 @@ import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetTagCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeCreatedEvent; @@ -53,6 +55,7 @@ import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException; import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException; import org.eclipse.hawkbit.repository.exception.TenantNotExistException; import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; +import org.eclipse.hawkbit.repository.jpa.TestHelper; import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaTarget; import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata; @@ -451,10 +454,12 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Finds a target by given ID and checks if all data is in the response (including the data defined as lazy).") @ExpectEvents({ @Expect(type = DistributionSetCreatedEvent.class, count = 2), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), // implicit lock @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 5), @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 2), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), @Expect(type = TargetAttributesRequestedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1) }) void findTargetByControllerIDWithDetails() { @@ -480,10 +485,12 @@ class TargetManagementTest extends AbstractJpaIntegrationTest { controllerManagement.findOrRegisterTargetIfItDoesNotExist("4711", LOCALHOST); final DistributionSetAssignmentResult result = assignDistributionSet(testDs1.getId(), "4711"); + TestHelper.implicitLock(testDs1); controllerManagement.addUpdateActionStatus( entityFactory.actionStatus().create(getFirstAssignedActionId(result)).status(Status.FINISHED)); assignDistributionSet(testDs2.getId(), "4711"); + TestHelper.implicitLock(testDs2); target = targetManagement.getByControllerID("4711").orElseThrow(IllegalStateException::new); // read data diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java index ec4295e26..6f81dca1e 100644 --- a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java +++ b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/AbstractDDiApiIntegrationTest.java @@ -30,6 +30,7 @@ import org.eclipse.hawkbit.ddi.json.model.DdiProgress; import org.eclipse.hawkbit.ddi.json.model.DdiResult; import org.eclipse.hawkbit.ddi.json.model.DdiStatus; import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration; +import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Artifact; import org.eclipse.hawkbit.repository.model.DistributionSet; @@ -372,5 +373,7 @@ public abstract class AbstractDDiApiIntegrationTest extends AbstractRestIntegrat downloadType, updateType); } - -} + static void implicitLock(final DistributionSet set) { + ((JpaDistributionSet) set).setOptLockRevision(set.getOptLockRevision() + 1); + } +} \ No newline at end of file diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java index af73a62d0..a4c04f769 100644 --- a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java +++ b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiArtifactDownloadTest.java @@ -79,7 +79,6 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest { // create ds final DistributionSet ds = testdataFactory.createDistributionSet(""); - assignDistributionSet(ds, targets); // create artifact final int artifactSize = 5 * 1024; @@ -87,6 +86,8 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest { final Artifact artifact = artifactManagement.create(new ArtifactUpload(new ByteArrayInputStream(random), ds.findFirstModuleByType(osType).get().getId(), "file1", false, artifactSize)); + assignDistributionSet(ds, targets); + // no artifact available mvc.perform(get("/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/123455", target.getControllerId(), getOsModule(ds))).andExpect(status().isNotFound()); @@ -206,14 +207,14 @@ public class DdiArtifactDownloadTest extends AbstractDDiApiIntegrationTest { // create ds final DistributionSet ds = testdataFactory.createDistributionSet(""); - assignDistributionSet(ds, target); - // create artifact final int artifactSize = 5 * 1024; final byte random[] = RandomUtils.nextBytes(artifactSize); final Artifact artifact = artifactManagement.create( new ArtifactUpload(new ByteArrayInputStream(random), getOsModule(ds), "file1", false, artifactSize)); + assignDistributionSet(ds, target); + // download final MvcResult result = mvc.perform(get( "/{tenant}/controller/v1/{controllerId}/softwaremodules/{softwareModuleId}/artifacts/{filename}.MD5SUM", diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiCancelActionTest.java b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiCancelActionTest.java index 6b9e66292..8def19903 100644 --- a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiCancelActionTest.java +++ b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiCancelActionTest.java @@ -28,19 +28,15 @@ import java.util.List; import org.eclipse.hawkbit.ddi.json.model.DdiResult; import org.eclipse.hawkbit.ddi.json.model.DdiStatus; import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants; -import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper; import org.eclipse.hawkbit.repository.jpa.repository.ActionStatusRepository; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; -import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.test.util.TestdataFactory; import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; import org.springframework.hateoas.MediaTypes; import org.springframework.http.MediaType; import org.springframework.integration.json.JsonPathUtils; @@ -95,6 +91,7 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest { final Long actionId = getFirstAssignedActionId( assignDistributionSet(ds.getId(), savedTarget.getControllerId())); + implicitLock(ds); final Action cancelAction = deploymentManagement.cancelAction(actionId); @@ -343,10 +340,13 @@ class DdiCancelActionTest extends AbstractDDiApiIntegrationTest { final Long actionId = getFirstAssignedActionId( assignDistributionSet(ds.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID)); + implicitLock(ds); final Long actionId2 = getFirstAssignedActionId( assignDistributionSet(ds2.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID)); + implicitLock(ds2); final Long actionId3 = getFirstAssignedActionId( assignDistributionSet(ds3.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID)); + implicitLock(ds3); assertThat(countActionStatusAll()).isEqualTo(3); diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiConfirmationBaseTest.java b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiConfirmationBaseTest.java index 2fb304ce8..721f994df 100644 --- a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiConfirmationBaseTest.java +++ b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiConfirmationBaseTest.java @@ -23,7 +23,9 @@ import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TenantConfigurationCreatedEvent; @@ -90,6 +92,7 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest { final List targetsAssignedToDs = assignDistributionSet(ds.getId(), savedTarget.getControllerId(), Action.ActionType.FORCED).getAssignedEntity().stream().map(Action::getTarget) .collect(Collectors.toList()); + implicitLock(ds); assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1); @@ -144,7 +147,11 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest { final Target target = testdataFactory.createTarget(); final DistributionSet distributionSet = testdataFactory.createDistributionSet(""); + final Long softwareModuleId = distributionSet.getModules().stream().findAny().get().getId(); + testdataFactory.createArtifacts(softwareModuleId); + assignDistributionSet(distributionSet.getId(), target.getName()); + final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId()) .getContent().get(0); @@ -152,8 +159,6 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest { performGet(CONFIRMATION_BASE_ACTION, MediaType.parseMediaType(DdiRestConstants.MEDIA_TYPE_CBOR), status().isOk(), tenantAware.getCurrentTenant(), target.getControllerId(), action.getId().toString()); - final Long softwareModuleId = distributionSet.getModules().stream().findAny().get().getId(); - testdataFactory.createArtifacts(softwareModuleId); // get artifacts performGet(SOFTWARE_MODULE_ARTIFACTS, MediaType.parseMediaType(DdiRestConstants.MEDIA_TYPE_CBOR), status().isOk(), tenantAware.getCurrentTenant(), target.getControllerId(), @@ -250,10 +255,12 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest { @Description("Controller sends a confirmed action state.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) void sendConfirmedActionStateFeedbackTest() throws Exception { @@ -398,11 +405,13 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest { @Description("Controller sends a denied action state.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), @Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) @@ -451,10 +460,12 @@ public class DdiConfirmationBaseTest extends AbstractDDiApiIntegrationTest { @Description("Test to verify that only a specific count of messages are returned based on the input actionHistory for getControllerDeploymentActionFeedback endpoint.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), @Expect(type = TenantConfigurationCreatedEvent.class, count = 1) }) void testActionHistoryCount() throws Exception { enableConfirmationFlow(); diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java index d0016ce2b..092fa2ce8 100644 --- a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java +++ b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java @@ -34,7 +34,9 @@ import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper; @@ -85,6 +87,9 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { final Target target = testdataFactory.createTarget(); final DistributionSet distributionSet = testdataFactory.createDistributionSet(""); + final Long softwareModuleId = distributionSet.getModules().stream().findAny().get().getId(); + testdataFactory.createArtifacts(softwareModuleId); + assignDistributionSet(distributionSet.getId(), target.getName()); final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId()) .getContent().get(0); @@ -93,8 +98,6 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { performGet(DEPLOYMENT_BASE, MediaType.parseMediaType(DdiRestConstants.MEDIA_TYPE_CBOR), status().isOk(), tenantAware.getCurrentTenant(), target.getControllerId(), action.getId().toString()); - final Long softwareModuleId = distributionSet.getModules().stream().findAny().get().getId(); - testdataFactory.createArtifacts(softwareModuleId); // get artifacts performGet(SOFTWARE_MODULE_ARTIFACTS, MediaType.parseMediaType(DdiRestConstants.MEDIA_TYPE_CBOR), status().isOk(), tenantAware.getCurrentTenant(), target.getControllerId(), @@ -120,22 +123,17 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { final Target target = testdataFactory.createTarget(); final DistributionSet distributionSet = testdataFactory.createDistributionSet(""); - assignDistributionSet(distributionSet.getId(), target.getName()); - final Long softwareModuleId = distributionSet.getModules().stream().findAny().get().getId(); - performGet(SOFTWARE_MODULE_ARTIFACTS, MediaType.APPLICATION_JSON, status().isOk(), - tenantAware.getCurrentTenant(), target.getControllerId(), softwareModuleId.toString()) - .andExpect(jsonPath("$", hasSize(0))); - testdataFactory.createArtifacts(softwareModuleId); + assignDistributionSet(distributionSet.getId(), target.getName()); + performGet(SOFTWARE_MODULE_ARTIFACTS, MediaType.APPLICATION_JSON, status().isOk(), tenantAware.getCurrentTenant(), target.getControllerId(), softwareModuleId.toString()) .andExpect(jsonPath("$", hasSize(3))) .andExpect(jsonPath("$.[?(@.filename=='filename0')]", hasSize(1))) .andExpect(jsonPath("$.[?(@.filename=='filename1')]", hasSize(1))) .andExpect(jsonPath("$.[?(@.filename=='filename2')]", hasSize(1))); - } @Test @@ -152,6 +150,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { final List targetsAssignedToDs = assignDistributionSet(ds.getId(), savedTarget.getControllerId(), ActionType.FORCED).getAssignedEntity().stream().map(Action::getTarget).collect(Collectors.toList()); + implicitLock(ds); assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1); @@ -250,6 +249,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { final List saved = assignDistributionSet(ds.getId(), savedTarget.getControllerId(), ActionType.SOFT) .getAssignedEntity().stream().map(Action::getTarget).collect(Collectors.toList()); + implicitLock(ds); assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1); final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId()) @@ -307,6 +307,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { final List saved = assignDistributionSet(ds.getId(), savedTarget.getControllerId(), ActionType.TIMEFORCED).getAssignedEntity().stream().map(Action::getTarget).collect(Collectors.toList()); + implicitLock(ds); assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1); final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId()) @@ -373,6 +374,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { final List saved = assignDistributionSet(ds.getId(), savedTarget.getControllerId(), ActionType.DOWNLOAD_ONLY).getAssignedEntity().stream().map(Action::getTarget) .collect(Collectors.toList()); + implicitLock(ds); assertThat(deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId())).hasSize(1); final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId()) @@ -516,8 +518,11 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { final DistributionSet ds3 = testdataFactory.createDistributionSet("3", true); final Long actionId1 = getFirstAssignedActionId(assignDistributionSet(ds1.getId(), DEFAULT_CONTROLLER_ID)); + implicitLock(ds1); final Long actionId2 = getFirstAssignedActionId(assignDistributionSet(ds2.getId(), DEFAULT_CONTROLLER_ID)); + implicitLock(ds2); final Long actionId3 = getFirstAssignedActionId(assignDistributionSet(ds3.getId(), DEFAULT_CONTROLLER_ID)); + implicitLock(ds3); findTargetAndAssertUpdateStatus(Optional.of(ds3), TargetUpdateStatus.PENDING, 3, Optional.empty()); assertThat(targetManagement.findByUpdateStatus(PageRequest.of(0, 10), TargetUpdateStatus.UNKNOWN)).hasSize(2); @@ -526,23 +531,25 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId1, getJsonClosedDeploymentActionFeedback(), status().isOk()); - findTargetAndAssertUpdateStatus(Optional.of(ds3), TargetUpdateStatus.PENDING, 2, Optional.of(ds1)); + findTargetAndAssertUpdateStatus(Optional.of(ds3), TargetUpdateStatus.PENDING, 2, + Optional.of(ds1)); assertStatusMessagesCount(4); // action2 done postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId2, getJsonClosedDeploymentActionFeedback(), status().isOk()); - findTargetAndAssertUpdateStatus(Optional.of(ds3), TargetUpdateStatus.PENDING, 1, Optional.of(ds2)); + findTargetAndAssertUpdateStatus(Optional.of(ds3), TargetUpdateStatus.PENDING, 1, + Optional.of(ds2)); assertStatusMessagesCount(5); // action3 done postDeploymentFeedback(DEFAULT_CONTROLLER_ID, actionId3, getJsonClosedDeploymentActionFeedback(), status().isOk()); - findTargetAndAssertUpdateStatus(Optional.of(ds3), TargetUpdateStatus.IN_SYNC, 0, Optional.of(ds3)); + findTargetAndAssertUpdateStatus(Optional.of(ds3), TargetUpdateStatus.IN_SYNC, 0, + Optional.of(ds3)); assertStatusMessagesCount(6); - } @Autowired @@ -594,6 +601,7 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { final DistributionSet ds = testdataFactory.createDistributionSet(""); final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds, Collections.singletonList(testdataFactory.createTarget(DEFAULT_CONTROLLER_ID)))); + implicitLock(ds); findTargetAndAssertUpdateStatus(Optional.of(ds), TargetUpdateStatus.PENDING, 1, Optional.empty()); // Now valid Feedback @@ -677,6 +685,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1) }) public void invalidIdInFeedbackReturnsBadRequest() throws Exception { @@ -695,6 +705,8 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1) }) public void missingResultAttributeInFeedbackReturnsBadRequest() throws Exception { @@ -718,10 +730,11 @@ public class DdiDeploymentBaseTest extends AbstractDDiApiIntegrationTest { @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 1) }) public void missingFinishedAttributeInFeedbackReturnsBadRequest() throws Exception { - final Target target = testdataFactory.createTarget("1080"); final DistributionSet ds = testdataFactory.createDistributionSet(""); assignDistributionSet(ds.getId(), "1080"); diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiInstalledBaseTest.java b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiInstalledBaseTest.java index 559f7cd55..10395839a 100644 --- a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiInstalledBaseTest.java +++ b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiInstalledBaseTest.java @@ -37,6 +37,7 @@ import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; @@ -83,6 +84,10 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest { public void installedBaseResourceCbor() throws Exception { final Target target = testdataFactory.createTarget(); final DistributionSet ds = testdataFactory.createDistributionSet(""); + + final Long softwareModuleId = ds.getModules().stream().findAny().get().getId(); + testdataFactory.createArtifacts(softwareModuleId); + final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds, target)); postDeploymentFeedback(target.getControllerId(), actionId, getJsonClosedDeploymentActionFeedback(), status().isOk()); @@ -91,8 +96,6 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest { performGet(INSTALLED_BASE, MediaType.parseMediaType(DdiRestConstants.MEDIA_TYPE_CBOR), status().isOk(), tenantAware.getCurrentTenant(), target.getControllerId(), actionId.toString()); - final Long softwareModuleId = ds.getModules().stream().findAny().get().getId(); - testdataFactory.createArtifacts(softwareModuleId); // get artifacts performGet(SOFTWARE_MODULE_ARTIFACTS, MediaType.parseMediaType(DdiRestConstants.MEDIA_TYPE_CBOR), status().isOk(), tenantAware.getCurrentTenant(), target.getControllerId(), @@ -359,25 +362,21 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest { public void artifactsOfInstalledActionExist() throws Exception { final Target target = createTargetAndAssertNoActiveActions(); final DistributionSet ds = testdataFactory.createDistributionSet(""); + + final Long softwareModuleId = ds.getModules().stream().findAny().get().getId(); + testdataFactory.createArtifacts(softwareModuleId); + final Long actionId = getFirstAssignedActionId(assignDistributionSet(ds, target)); postDeploymentFeedback(target.getControllerId(), actionId, getJsonClosedDeploymentActionFeedback(), status().isOk()); - final Long softwareModuleId = ds.getModules().stream().findAny().get().getId(); - performGet(SOFTWARE_MODULE_ARTIFACTS, MediaType.APPLICATION_JSON, status().isOk(), - tenantAware.getCurrentTenant(), target.getControllerId(), softwareModuleId.toString()) - .andExpect(jsonPath("$", hasSize(0))); - - testdataFactory.createArtifacts(softwareModuleId); - performGet(SOFTWARE_MODULE_ARTIFACTS, MediaType.APPLICATION_JSON, status().isOk(), tenantAware.getCurrentTenant(), target.getControllerId(), softwareModuleId.toString()) .andExpect(jsonPath("$", hasSize(3))) .andExpect(jsonPath("$.[?(@.filename=='filename0')]", hasSize(1))) .andExpect(jsonPath("$.[?(@.filename=='filename1')]", hasSize(1))) .andExpect(jsonPath("$.[?(@.filename=='filename2')]", hasSize(1))); - } private static Stream actionTypeForDeployment() { @@ -389,12 +388,13 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest { @Description("Test forced deployment to a controller. Checks that action is represented as installedBase after installation.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 5), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetAttributesRequestedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), - @Expect(type = SoftwareModuleUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) }) public void deploymentActionInInstalledBase(final Action.ActionType actionType) throws Exception { // Prepare test data @@ -439,11 +439,13 @@ public class DdiInstalledBaseTest extends AbstractDDiApiIntegrationTest { @Description("Test download-only deployment to a controller. Checks that download-only is not represented as installedBase.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetAttributesRequestedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), @Expect(type = TargetPollEvent.class, count = 2) }) public void deploymentDownloadOnlyActionNotInInstalledBase() throws Exception { // Prepare test data diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java index 2b83f3af6..3a58ce50c 100644 --- a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java +++ b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java @@ -45,8 +45,10 @@ import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTypeCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleTypeCreatedEvent; +import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TenantConfigurationCreatedEvent; @@ -230,9 +232,11 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest { @Expect(type = TargetAssignDistributionSetEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 3), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 2), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), // implicit lock @Expect(type = ActionCreatedEvent.class, count = 2), - @Expect(type = TargetAttributesRequestedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 6) }) + @Expect(type = TargetAttributesRequestedEvent.class, count = 1) }) void rootRsNotModified() throws Exception { final String controllerId = "4711"; final String etag = mvc.perform(get(CONTROLLER_BASE, tenantAware.getCurrentTenant(), controllerId)) @@ -386,10 +390,12 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest { @Description("Controller trys to finish an update process after it has been finished by an error action status.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), - @Expect(type = TargetUpdatedEvent.class, count = 2), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetUpdatedEvent.class, count = 2) }) void tryToFinishAnUpdateProcessAfterItHasBeenFinished() throws Exception { final DistributionSet ds = testdataFactory.createDistributionSet(""); Target savedTarget = testdataFactory.createTarget("911"); @@ -410,10 +416,12 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest { @Description("Controller sends attribute update request after device successfully closed software update.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 2), @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 6), @Expect(type = TargetPollEvent.class, count = 4), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), @Expect(type = TargetAttributesRequestedEvent.class, count = 1) }) void attributeUpdateRequestSendingAfterSuccessfulDeployment() throws Exception { final DistributionSet ds = testdataFactory.createDistributionSet("1"); @@ -488,11 +496,13 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest { @Description("Test to verify that only a specific count of messages are returned based on the input actionHistory for getControllerDeploymentActionFeedback endpoint.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2), - @Expect(type = TargetAttributesRequestedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAttributesRequestedEvent.class, count = 1) }) void testActionHistoryCount() throws Exception { final DistributionSet ds = testdataFactory.createDistributionSet(""); Target savedTarget = testdataFactory.createTarget("911"); @@ -524,11 +534,13 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest { @Description("Test to verify that a zero input value of actionHistory results in no action history appended for getControllerDeploymentActionFeedback endpoint.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2), - @Expect(type = TargetAttributesRequestedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAttributesRequestedEvent.class, count = 1) }) void testActionHistoryZeroInput() throws Exception { final DistributionSet ds = testdataFactory.createDistributionSet(""); Target savedTarget = testdataFactory.createTarget("911"); @@ -560,11 +572,13 @@ class DdiRootControllerTest extends AbstractDDiApiIntegrationTest { @Description("Test to verify that entire action history is returned if the input value for actionHistory is -1, for getControllerDeploymentActionFeedback endpoint.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DistributionSetCreatedEvent.class, count = 1), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), + @Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock + @Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2), - @Expect(type = TargetAttributesRequestedEvent.class, count = 1), - @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) + @Expect(type = TargetAttributesRequestedEvent.class, count = 1) }) void testActionHistoryNegativeInput() throws Exception { final DistributionSet ds = testdataFactory.createDistributionSet(""); Target savedTarget = testdataFactory.createTarget("911"); diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/AbstractManagementApiIntegrationTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/AbstractManagementApiIntegrationTest.java index d207d9e9e..35d19e702 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/AbstractManagementApiIntegrationTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/AbstractManagementApiIntegrationTest.java @@ -15,7 +15,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType; import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration; +import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet; import org.eclipse.hawkbit.repository.model.BaseEntity; +import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.NamedEntity; import org.eclipse.hawkbit.repository.model.NamedVersionedEntity; import org.eclipse.hawkbit.repository.model.Tag; @@ -223,4 +225,12 @@ public abstract class AbstractManagementApiIntegrationTest extends AbstractRestI return new JSONObject(); } -} + // the set is a candidate for implicitly locking. So, lock it if not already locked + // set lock flag to true to avoid re-locking + static void implicitLock(final DistributionSet set) { + if (!set.isLocked()) { + ((JpaDistributionSet) set).setOptLockRevision(set.getOptLockRevision() + 1); + ((JpaDistributionSet) set).lock(); + } + } +} \ No newline at end of file diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java index 0d6d57d14..7959057ba 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetFilterQueryResourceTest.java @@ -30,7 +30,7 @@ import org.eclipse.hawkbit.exception.SpServerError; import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType; import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException; -import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; +import org.eclipse.hawkbit.repository.exception.DeletedException; import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException; import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException; import org.eclipse.hawkbit.repository.model.Action; @@ -427,20 +427,21 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInt @Description("Ensures that the update of a target filter query results in a HTTP Forbidden error (403) " + "if the updated query addresses too many targets.") public void updateTargetFilterQueryWithQueryThatExceedsQuota() throws Exception { - // create targets final int maxTargets = quotaManagement.getMaxTargetsPerAutoAssignment(); testdataFactory.createTargets(maxTargets + 1, "target"); + final TargetFilterQuery filterQuery = createSingleTargetFilterQuery("1", "controllerId==target1"); + // create the filter query and the distribution set final DistributionSet set = testdataFactory.createDistributionSet(); - final TargetFilterQuery filterQuery = createSingleTargetFilterQuery("1", "controllerId==target1"); // assign the auto-assign distribution set, this should work mvc.perform( post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId() + "/autoAssignDS") .content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON)) .andDo(print()).andExpect(status().isOk()); + implicitLock(set); final TargetFilterQuery updatedFilterQuery = targetFilterQueryManagement.get(filterQuery.getId()).get(); @@ -469,23 +470,18 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInt enableConfirmationFlow(); } - final DistributionSet set = testdataFactory.createDistributionSet(); final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery); + // set will be locked after first assignment + final DistributionSet set = testdataFactory.createDistributionSet(); + verifyAutoAssignmentWithoutActionType(tfq, set, confirmationRequired); - verifyAutoAssignmentWithForcedActionType(tfq, set, confirmationRequired); - verifyAutoAssignmentWithSoftActionType(tfq, set, confirmationRequired); - verifyAutoAssignmentWithTimeForcedActionType(tfq, set); - verifyAutoAssignmentWithDownloadOnlyActionType(tfq, set, confirmationRequired); - verifyAutoAssignmentWithUnknownActionType(tfq, set); - verifyAutoAssignmentWithIncompleteDs(tfq); - verifyAutoAssignmentWithSoftDeletedDs(tfq); } @@ -500,8 +496,9 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInt enableConfirmationFlow(); } - final DistributionSet set = testdataFactory.createDistributionSet(); final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery); + // set will be implicitly locked + final DistributionSet set = testdataFactory.createDistributionSet(); // do not provide something about the confirmation verifyAutoAssignmentByActionType(tfq, set, null, null); @@ -551,6 +548,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInt mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS") .content(jsonObject.toString()).contentType(MediaType.APPLICATION_JSON)).andDo(print()) .andExpect(status().isOk()); + implicitLock(set); final TargetFilterQuery updatedFilterQuery = targetFilterQueryManagement.get(tfq.getId()).get(); final MgmtActionType expectedActionType = actionType != null ? actionType : MgmtActionType.FORCED; @@ -623,8 +621,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInt mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS") .content("{\"id\":" + softDeletedDs.getId() + "}").contentType(MediaType.APPLICATION_JSON)) .andDo(print()).andExpect(status().isNotFound()) - .andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(EntityNotFoundException.class.getName()))) - .andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_REPO_ENTITY_NOT_EXISTS.getKey()))); + .andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(DeletedException.class.getName()))) + .andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_DELETED.getKey()))); } @Test @@ -677,7 +675,6 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInt @Test @Description("Ensures that the deletion of auto-assignment distribution set works as intended, deleting the auto-assignment action type as well") public void deleteAutoAssignDistributionSetOfTargetFilterQuery() throws Exception { - final String knownQuery = "name==test06"; final String knownName = "filter06"; final String dsName = "testDS"; @@ -686,6 +683,7 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInt final TargetFilterQuery tfq = createSingleTargetFilterQuery(knownName, knownQuery); targetFilterQueryManagement .updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(tfq.getId()).ds(set.getId())); + implicitLock(set); final TargetFilterQuery updatedFilterQuery = targetFilterQueryManagement.get(tfq.getId()).get(); diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java index be3b0f823..642bf90cd 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java @@ -92,7 +92,6 @@ import org.springframework.data.domain.Sort.Direction; import org.springframework.hateoas.MediaTypes; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.orm.jpa.vendor.Database; import org.springframework.test.web.servlet.MvcResult; import com.jayway.jsonpath.JsonPath; @@ -1391,10 +1390,9 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { } @Test - @Description("Verfies that a DS to target assignment is reflected by the repository and that repeating " + @Description("Verifies that a DS to target assignment is reflected by the repository and that repeating " + "the assignment does not change the target.") void assignDistributionSetToTarget() throws Exception { - Target target = testdataFactory.createTarget(); final DistributionSet set = testdataFactory.createDistributionSet("one"); @@ -1403,6 +1401,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andExpect(jsonPath("assigned", equalTo(1))).andExpect(jsonPath("alreadyAssigned", equalTo(0))) .andExpect(jsonPath("total", equalTo(1))); + implicitLock(set); assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).get()).isEqualTo(set); target = targetManagement.getByControllerID(target.getControllerId()).get(); @@ -1425,7 +1424,6 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { @Description("Ensures that confirmation option is considered in assignment request.") void assignDistributionSetToTargetWithConfirmationOptions(final boolean confirmationFlowActive, final Boolean confirmationRequired) throws Exception { - final Target target = testdataFactory.createTarget(); final DistributionSet set = testdataFactory.createDistributionSet("one"); @@ -1444,6 +1442,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andExpect(jsonPath("assigned", equalTo(1))).andExpect(jsonPath("alreadyAssigned", equalTo(0))) .andExpect(jsonPath("total", equalTo(1))); + implicitLock(set); assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).get()).isEqualTo(set); @@ -1468,7 +1467,6 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { @Test @Description("Verfies that a DOWNLOAD_ONLY DS to target assignment is properly handled") void assignDownloadOnlyDistributionSetToTarget() throws Exception { - final Target target = testdataFactory.createTarget(); final DistributionSet set = testdataFactory.createDistributionSet("one"); @@ -1477,6 +1475,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andExpect(jsonPath("assigned", equalTo(1))).andExpect(jsonPath("alreadyAssigned", equalTo(0))) .andExpect(jsonPath("total", equalTo(1))); + implicitLock(set); assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).get()).isEqualTo(set); final Slice actions = deploymentManagement.findActionsByTarget("targetExist", PageRequest.of(0, 100)); @@ -1488,7 +1487,6 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { @Description("Verfies that an offline DS to target assignment is reflected by the repository and that repeating " + "the assignment does not change the target.") void offlineAssignDistributionSetToTarget() throws Exception { - Target target = testdataFactory.createTarget(); final DistributionSet set = testdataFactory.createDistributionSet("one"); @@ -1498,6 +1496,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andExpect(jsonPath("assigned", equalTo(1))).andExpect(jsonPath("alreadyAssigned", equalTo(0))) .andExpect(jsonPath("total", equalTo(1))); + implicitLock(set); assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).get()).isEqualTo(set); assertThat(deploymentManagement.getInstalledDistributionSet(target.getControllerId()).get()).isEqualTo(set); @@ -1518,7 +1517,6 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { @Test void assignDistributionSetToTargetWithActionTimeForcedAndTime() throws Exception { - final Target target = testdataFactory.createTarget("fsdfsd"); final DistributionSet set = testdataFactory.createDistributionSet("one"); @@ -1529,6 +1527,7 @@ class MgmtTargetResourceTest extends AbstractManagementApiIntegrationTest { mvc.perform(post(MgmtRestConstants.TARGET_V1_REQUEST_MAPPING + "/fsdfsd/assignedDS").content(body) .contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()); + implicitLock(set); final List findActiveActionsByTarget = deploymentManagement .findActiveActionsByTarget(PAGE, target.getControllerId()).getContent(); diff --git a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java index 0616d971f..2131818a2 100644 --- a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java +++ b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java @@ -93,6 +93,8 @@ public class ResponseExceptionHandler { ERROR_TO_HTTP_STATUS.put(SpServerError.SP_TARGET_TYPE_KEY_OR_NAME_REQUIRED, HttpStatus.BAD_REQUEST); ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_INVALID, HttpStatus.BAD_REQUEST); ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_INCOMPLETE, HttpStatus.BAD_REQUEST); + ERROR_TO_HTTP_STATUS.put(SpServerError.SP_LOCKED, HttpStatus.LOCKED); + ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DELETED, HttpStatus.NOT_FOUND); ERROR_TO_HTTP_STATUS.put(SpServerError.SP_STOP_ROLLOUT_FAILED, HttpStatus.LOCKED); }