[#1580] Software Module & Distribution Set lock: implicit (#1649)

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2024-02-18 23:01:55 +02:00
committed by GitHub
parent 94576bd6fe
commit 9e76223a91
34 changed files with 630 additions and 293 deletions

View File

@@ -329,10 +329,11 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
}
private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(final String initiatedBy,
final Long dsID, final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
final Long dsId, final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
final AbstractDsAssignmentStrategy assignmentStrategy) {
final RetryCallback<DistributionSetAssignmentResult, ConcurrencyFailureException> retryCallback = retryContext -> assignDistributionSetToTargets(
initiatedBy, dsID, targetsWithActionType, actionMessage, assignmentStrategy);
final RetryCallback<DistributionSetAssignmentResult, ConcurrencyFailureException> 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<TargetWithActionType> 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<String> 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<JpaTarget> targetEntities = assignmentStrategy.findTargetsForAssignment(existingTargetIds,
distributionSetEntity.getId());
distributionSet.getId());
if (targetEntities.isEmpty()) {
return allTargetsAlreadyAssignedResult(distributionSetEntity, existingTargetIds.size());
return allTargetsAlreadyAssignedResult(distributionSet, existingTargetIds.size());
}
final List<TargetWithActionType> existingTargetsWithActionType = targetsWithActionType.stream()
.filter(target -> existingTargetIds.contains(target.getControllerId())).toList();
final List<JpaAction> 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(

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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)) {

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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();

View File

@@ -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<Target> updateTargets = testdataFactory.createTargets("update1", "update2", "update3");
final List<Target> readTargets = testdataFactory.createTargets("read1", "read2", "read3", "read4");
final List<Target> hiddenTargets = testdataFactory.createTargets("hidden1", "hidden2", "hidden3", "hidden4",
"hidden5");
final List<Target> 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<Target> 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()),

View File

@@ -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<Target> 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<Target> 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<Target> 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<Target> targetsA = createTargetsAndAutoAssignDistSet(targetDsAIdPref, 5, distributionSet,
ActionType.FORCED);
TestHelper.implicitLock(distributionSet);
final List<Target> targetsB = createTargetsAndAutoAssignDistSet(targetDsBIdPref, 10, distributionSet,
ActionType.SOFT);
final List<Target> targetsC = createTargetsAndAutoAssignDistSet(targetDsCIdPref, 10, distributionSet,
@@ -498,4 +503,4 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest {
.findAll(ActionSpecifications.byDistributionSetId(distributionSetId), pageable)
.map(Action.class::cast);
}
}
}

View File

@@ -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");

View File

@@ -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<String> 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<JpaAction> 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<Target> 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<Target> content = targetManagement.findAll(Pageable.unpaged()).getContent();
@@ -1593,6 +1620,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
final List<DeploymentRequest> deploymentRequests = Arrays.asList(deployment1, deployment2);
deploymentManagement.assignDistributionSets(deploymentRequests);
TestHelper.implicitLock(ds);
final Optional<DistributionSet> 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<Action> findActionsByDistributionSet(final Pageable pageable, final long distributionSetId) {
@@ -1744,4 +1772,4 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
return undeployedTargetIDs;
}
}
}
}

View File

@@ -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

View File

@@ -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<DistributionSet> buildDistributionSets = testdataFactory.createDistributionSets("dsOrder", 10);
final List<Target> 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

View File

@@ -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) })

View File

@@ -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),

View File

@@ -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<DistributionSet> 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

View File

@@ -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");
}
}

View File

@@ -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