diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java index 83a45ef8d..c8e74339a 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java @@ -12,16 +12,19 @@ import static org.eclipse.hawkbit.repository.RepositoryConstants.MAX_ACTION_COUN import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED; import java.net.URI; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Optional; import java.util.Set; import java.util.function.Function; +import java.util.function.Supplier; import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import com.google.common.collect.Iterables; import org.eclipse.hawkbit.api.ApiType; import org.eclipse.hawkbit.api.ArtifactUrl; import org.eclipse.hawkbit.api.ArtifactUrlHandler; @@ -50,7 +53,7 @@ import org.eclipse.hawkbit.repository.event.remote.MultiActionEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent; -import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent; +import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.ActionProperties; import org.eclipse.hawkbit.repository.model.Artifact; @@ -70,6 +73,8 @@ import org.springframework.cloud.bus.ServiceMatcher; import org.springframework.cloud.bus.event.RemoteApplicationEvent; import org.springframework.context.event.EventListener; import org.springframework.data.domain.PageRequest; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.util.CollectionUtils; /** @@ -84,6 +89,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { private static final Logger LOG = LoggerFactory.getLogger(AmqpMessageDispatcherService.class); + private static final int MAX_PROCESSING_SIZE = 1000; + private final ArtifactUrlHandler artifactUrlHandler; private final AmqpMessageSenderService amqpSenderService; private final SystemSecurityContext systemSecurityContext; @@ -177,14 +184,16 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { } private List getTargetsWithoutPendingCancellations(final Set controllerIds) { - return targetManagement.getByControllerID(controllerIds).stream().filter(target -> { - if (hasPendingCancellations(target.getControllerId())) { - LOG.debug("Target {} has pending cancellations. Will not send update message to it.", - target.getControllerId()); - return false; - } - return true; - }).collect(Collectors.toList()); + return partitionedParallelExecution(controllerIds, partition -> { + return targetManagement.getByControllerID(partition).stream().filter(target -> { + if (hasPendingCancellations(target.getControllerId())) { + LOG.debug("Target {} has pending cancellations. Will not send update message to it.", + target.getControllerId()); + return false; + } + return true; + }).collect(Collectors.toList()); + }); } private void sendUpdateMessageToTarget(final TargetAssignDistributionSetEvent assignedEvent, @@ -319,18 +328,45 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { return; } - final Optional eventEntity = cancelEvent.getEntity(); - if (eventEntity.isPresent()) { - final Target target = eventEntity.get(); - sendCancelMessageToTarget(cancelEvent.getTenant(), target.getControllerId(), cancelEvent.getActionId(), - target.getAddress()); - } else { - LOG.warn( - "Cannot process the received CancelTargetAssignmentEvent with action ID {} because the referenced target with ID {} does no longer exist.", - cancelEvent.getActionId(), cancelEvent.getEntityId()); - } + final List eventTargets = partitionedParallelExecution(cancelEvent.getActions().keySet(), + targetManagement::getByControllerID); + + eventTargets.forEach(target -> { + cancelEvent.getActionPropertiesForController(target.getControllerId()).map(ActionProperties::getId) + .ifPresent(actionId -> { + sendCancelMessageToTarget(cancelEvent.getTenant(), target.getControllerId(), actionId, + target.getAddress()); + }); + }); } + private static List partitionedParallelExecution(final Collection controllerIds, + final Function, List> loadingFunction) { + // Ensure not exceeding the max value of MAX_PROCESSING_SIZE + if (controllerIds.size() > MAX_PROCESSING_SIZE) { + // Split the provided collection + final Iterable> partitions = Iterables.partition(controllerIds, MAX_PROCESSING_SIZE); + // Preserve the security context because it gets lost when executing + // loading calls in new threads + final SecurityContext context = SecurityContextHolder.getContext(); + // Handling remote request in parallel streams + return StreamSupport.stream(partitions.spliterator(), true) // + .flatMap(partition -> withSecurityContext(() -> loadingFunction.apply(partition), context).stream()) + .collect(Collectors.toList()); + } + return loadingFunction.apply(controllerIds); + } + + private static T withSecurityContext(final Supplier callable, final SecurityContext securityContext) { + final SecurityContext oldContext = SecurityContextHolder.getContext(); + try { + SecurityContextHolder.setContext(securityContext); + return callable.get(); + } finally { + SecurityContextHolder.setContext(oldContext); + } + } + /** * Method to send a message to a RabbitMQ Exchange after a Target was * deleted. diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java index 2a9587b33..e71011268 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java @@ -11,6 +11,7 @@ package org.eclipse.hawkbit.amqp; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.File; @@ -37,7 +38,7 @@ import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent; -import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent; +import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent; import org.eclipse.hawkbit.repository.jpa.RepositoryApplicationConfiguration; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Artifact; @@ -230,12 +231,14 @@ class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest { @Test @Description("Verifies that send cancel event works") void testSendCancelRequest() { + final Action action = mock(Action.class); + when(action.getId()).thenReturn(1L); + when(action.getTarget()).thenReturn(testTarget); final CancelTargetAssignmentEvent cancelTargetAssignmentDistributionSetEvent = new CancelTargetAssignmentEvent( - testTarget, 1L, serviceMatcher.getServiceId()); + action, serviceMatcher.getServiceId()); amqpMessageDispatcherService .targetCancelAssignmentToDistributionSet(cancelTargetAssignmentDistributionSetEvent); - final Message sendMessage = createArgumentCapture( - cancelTargetAssignmentDistributionSetEvent.getEntity().get().getAddress()); + final Message sendMessage = createArgumentCapture(AMQP_URI); assertCancelMessage(sendMessage); } 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 062eaffa1..21edf1e9a 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 @@ -48,7 +48,7 @@ import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent; 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.CancelTargetAssignmentEvent; +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.RolloutCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupCreatedEvent; 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 62eb9e741..203022102 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 @@ -41,7 +41,7 @@ import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEven 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.CancelTargetAssignmentEvent; +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.SoftwareModuleCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent; diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/AbstractAssignmentEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/AbstractAssignmentEvent.java new file mode 100644 index 000000000..e02606321 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/AbstractAssignmentEvent.java @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2022 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository.event.remote; + +import org.eclipse.hawkbit.repository.model.Action; +import org.eclipse.hawkbit.repository.model.ActionProperties; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Abstract class providing information about an assignment. + */ +public abstract class AbstractAssignmentEvent extends RemoteTenantAwareEvent { + + private static final long serialVersionUID = 1L; + + private final Map actions = new HashMap<>(); + + /** + * Default constructor. + */ + protected AbstractAssignmentEvent() { + // for serialization libs like jackson + } + + protected AbstractAssignmentEvent(final Object source, final Action a, final String applicationId) { + super(source, a.getTenant(), applicationId); + actions.put(a.getTarget().getControllerId(), new ActionProperties(a)); + } + + protected AbstractAssignmentEvent(final Object source, final String tenant, final List a, + final String applicationId) { + super(source, tenant, applicationId); + actions.putAll(a.stream() + .collect(Collectors.toMap(action -> action.getTarget().getControllerId(), ActionProperties::new))); + } + + public Map getActions() { + return actions; + } + + public Optional getActionPropertiesForController(final String controllerId) { + return Optional.ofNullable(actions.get(controllerId)); + } + +} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/CancelTargetAssignmentEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/CancelTargetAssignmentEvent.java new file mode 100644 index 000000000..3050216e9 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/CancelTargetAssignmentEvent.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2022 Bosch.IO GmbH and others. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package org.eclipse.hawkbit.repository.event.remote; + +import java.util.List; + +import org.eclipse.hawkbit.repository.model.Action; + +/** + * Event that gets sent when the assignment of a distribution set to a target + * gets canceled. + */ +public class CancelTargetAssignmentEvent extends AbstractAssignmentEvent { + + private static final long serialVersionUID = 1L; + + /** + * Default constructor. + */ + public CancelTargetAssignmentEvent() { + // for serialization libs like jackson + } + + public CancelTargetAssignmentEvent(final Action a, final String applicationId) { + super(applicationId, a, applicationId); + } + + public CancelTargetAssignmentEvent(final String tenant, final List a, final String applicationId) { + super(applicationId, tenant, a, applicationId); + + } + +} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/TargetAssignDistributionSetEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/TargetAssignDistributionSetEvent.java index a61b60770..2dbfb669e 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/TargetAssignDistributionSetEvent.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/TargetAssignDistributionSetEvent.java @@ -9,19 +9,16 @@ package org.eclipse.hawkbit.repository.event.remote; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; import org.eclipse.hawkbit.repository.model.Action; -import org.eclipse.hawkbit.repository.model.ActionProperties; /** * TenantAwareEvent that gets sent when a distribution set gets assigned to a * target. */ -public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent { +public class TargetAssignDistributionSetEvent extends AbstractAssignmentEvent { private static final long serialVersionUID = 1L; @@ -29,8 +26,6 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent { private boolean maintenanceWindowAvailable; - private final Map actions = new HashMap<>(); - /** * Default constructor. */ @@ -54,12 +49,12 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent { */ public TargetAssignDistributionSetEvent(final String tenant, final long distributionSetId, final List a, final String applicationId, final boolean maintenanceWindowAvailable) { - super(distributionSetId, tenant, applicationId); + super(distributionSetId, tenant, + a.stream().filter(action -> action.getDistributionSet().getId().longValue() == distributionSetId) + .collect(Collectors.toList()), + applicationId); this.distributionSetId = distributionSetId; this.maintenanceWindowAvailable = maintenanceWindowAvailable; - actions.putAll(a.stream().filter(action -> action.getDistributionSet().getId().longValue() == distributionSetId) - .collect(Collectors.toMap(action -> action.getTarget().getControllerId(), ActionProperties::new))); - } /** @@ -83,8 +78,4 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent { return maintenanceWindowAvailable; } - public Map getActions() { - return actions; - } - } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/CancelTargetAssignmentEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/CancelTargetAssignmentEvent.java deleted file mode 100644 index ea105ac9c..000000000 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/entity/CancelTargetAssignmentEvent.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - */ -package org.eclipse.hawkbit.repository.event.remote.entity; - -import org.eclipse.hawkbit.repository.model.Target; - -/** - * Event that gets sent when the assignment of a distribution set to a target - * gets canceled. - */ -public class CancelTargetAssignmentEvent extends RemoteEntityEvent { - - private static final long serialVersionUID = 1L; - - private Long actionId; - - /** - * Default constructor. - */ - public CancelTargetAssignmentEvent() { - // for serialization libs like jackson - } - - /** - * Constructor. - * - * @param baseEntity - * the target - * @param actionId - * the actionId - * @param applicationId - * the origin application id - */ - public CancelTargetAssignmentEvent(final Target baseEntity, final Long actionId, final String applicationId) { - super(baseEntity, applicationId); - this.actionId = actionId; - } - - /** - * @return the action id of the assignment - */ - public Long getActionId() { - return actionId; - } - -} diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventType.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventType.java index 092d3b129..927652d0b 100644 --- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventType.java +++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventType.java @@ -34,7 +34,7 @@ import org.eclipse.hawkbit.repository.event.remote.TargetTypeDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.TenantConfigurationDeletedEvent; 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.CancelTargetAssignmentEvent; +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.DistributionSetTagCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagUpdatedEvent; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java index 1bed104fd..08e7c280f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java @@ -9,6 +9,7 @@ package org.eclipse.hawkbit.repository.jpa; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.Set; @@ -17,7 +18,7 @@ import java.util.stream.Collectors; import org.eclipse.hawkbit.repository.QuotaManagement; import org.eclipse.hawkbit.repository.RepositoryConstants; -import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent; +import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; @@ -30,11 +31,15 @@ import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; +import org.eclipse.hawkbit.repository.model.Rollout; +import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; /** * {@link DistributionSet} to {@link Target} assignment strategy as utility for @@ -144,19 +149,22 @@ public abstract class AbstractDsAssignmentStrategy { .findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetNotRequiredMigrationStep( targetsIds, Action.Status.CANCELING); - return activeActions.stream().map(action -> { + final List targetIds = activeActions.stream().map(action -> { action.setStatus(Status.CANCELING); // document that the status has been retrieved actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, System.currentTimeMillis(), - RepositoryConstants.SERVER_MESSAGE_PREFIX + "cancel obsolete action due to new update")); + RepositoryConstants.SERVER_MESSAGE_PREFIX + "cancel obsolete action due to new update")); actionRepository.save(action); - cancelAssignDistributionSetEvent(action.getTarget(), action.getId()); - return action.getTarget().getId(); }).collect(Collectors.toList()); + if (!activeActions.isEmpty()) { + cancelAssignDistributionSetEvent(Collections.unmodifiableList(activeActions)); + } + + return targetIds; } /** @@ -188,20 +196,28 @@ public abstract class AbstractDsAssignmentStrategy { } /** - * Sends the {@link CancelTargetAssignmentEvent} for a specific target to + * Sends the {@link CancelTargetAssignmentEvent} for a specific action to * the eventPublisher. * - * @param target - * the Target which has been assigned to a distribution set - * @param actionId - * the action id of the assignment + * @param action + * the action of the assignment */ - void cancelAssignDistributionSetEvent(final Target target, final Long actionId) { + protected void cancelAssignDistributionSetEvent(final Action action) { afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher().publishEvent( - new CancelTargetAssignmentEvent(target, actionId, eventPublisherHolder.getApplicationId()))); + new CancelTargetAssignmentEvent(action, eventPublisherHolder.getApplicationId()))); } - JpaAction createTargetAction(final String initiatedBy, final TargetWithActionType targetWithActionType, + private void cancelAssignDistributionSetEvent(final List actions) { + if (CollectionUtils.isEmpty(actions)) { + return; + } + final String tenant = actions.get(0).getTenant(); + afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher() + .publishEvent(new CancelTargetAssignmentEvent(tenant, + actions, eventPublisherHolder.getApplicationId()))); + } + + public JpaAction createTargetAction(final String initiatedBy, final TargetWithActionType targetWithActionType, final List targets, final JpaDistributionSet set) { final Optional optTarget = targets.stream() .filter(t -> t.getControllerId().equals(targetWithActionType.getControllerId())).findFirst(); @@ -227,18 +243,30 @@ public abstract class AbstractDsAssignmentStrategy { }); } - JpaActionStatus createActionStatus(final JpaAction action, final String actionMessage) { + public JpaActionStatus createActionStatus(final JpaAction action, final String actionMessage) { final JpaActionStatus actionStatus = new JpaActionStatus(); actionStatus.setAction(action); actionStatus.setOccurredAt(action.getCreatedAt()); - if (actionMessage != null) { + if (StringUtils.hasText(actionMessage)) { actionStatus.addMessage(actionMessage); + } else { + actionStatus.addMessage(getActionMessage(action)); } return actionStatus; } + private static String getActionMessage(final Action action) { + final RolloutGroup rolloutGroup = action.getRolloutGroup(); + if (rolloutGroup != null) { + final Rollout rollout = rolloutGroup.getRollout(); + return String.format("Initiated by Rollout Group '%s' [Rollout %s:%s]", rolloutGroup.getName(), + rollout.getName(), rollout.getId()); + } + return String.format("Assignment initiated by user '%s'", action.getInitiatedBy()); + } + private void assertActionsPerTargetQuota(final Target target, final int requested) { final int quota = quotaManagement.getMaxActionsPerTarget(); QuotaHelper.assertAssignmentQuota(target.getId(), requested, quota, Action.class, Target.class, diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java index efe5864b0..e05d8d2cf 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java @@ -50,7 +50,7 @@ import org.eclipse.hawkbit.repository.UpdateMode; import org.eclipse.hawkbit.repository.builder.ActionStatusCreate; import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent; -import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent; +import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent; import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; @@ -1041,7 +1041,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, System.currentTimeMillis(), "manual cancelation requested")); final Action saveAction = actionRepository.save(action); - cancelAssignDistributionSetEvent((JpaTarget) action.getTarget(), action.getId()); + cancelAssignDistributionSetEvent(action); return saveAction; } else { @@ -1074,9 +1074,9 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont } } - private void cancelAssignDistributionSetEvent(final JpaTarget target, final Long actionId) { + private void cancelAssignDistributionSetEvent(final Action action) { afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher().publishEvent( - new CancelTargetAssignmentEvent(target, actionId, eventPublisherHolder.getApplicationId()))); + new CancelTargetAssignmentEvent(action, eventPublisherHolder.getApplicationId()))); } // for testing diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index ef3b724d2..575c4f12c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -72,6 +72,8 @@ import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType; import org.eclipse.hawkbit.repository.model.DistributionSetType; +import org.eclipse.hawkbit.repository.model.Rollout; +import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetType; @@ -580,19 +582,28 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl if (rolloutGroupActions.getContent().isEmpty()) { return 0L; } + + final List newTargetAssignments = handleTargetAssignments(rolloutGroupActions); - final List targetAssignments = rolloutGroupActions.getContent().stream().map(JpaAction.class::cast) - .map(this::closeActionIfSetWasAlreadyAssigned).filter(Objects::nonNull) - .map(this::startScheduledActionIfNoCancelationHasToBeHandledFirst).filter(Objects::nonNull) - .collect(Collectors.toList()); - - if (!targetAssignments.isEmpty()) { - onlineDsAssignmentStrategy.sendDeploymentEvents(distributionSetId, targetAssignments); + if (!newTargetAssignments.isEmpty()) { + onlineDsAssignmentStrategy.sendDeploymentEvents(distributionSetId, newTargetAssignments); } return rolloutGroupActions.getTotalElements(); }); } + + private List handleTargetAssignments(final Page rolloutGroupActions) { + // Close actions already assigned and collect pending assignments + final List pendingTargetAssignments = rolloutGroupActions.getContent().stream() + .map(JpaAction.class::cast).map(this::closeActionIfSetWasAlreadyAssigned).filter(Objects::nonNull) + .collect(Collectors.toList()); + if (pendingTargetAssignments.isEmpty()) { + return new ArrayList<>(pendingTargetAssignments); + } + // check if old actions needs to be canceled first + return startScheduledActionsAndHandleOpenCancellationFirst(pendingTargetAssignments); + } private Page findActionsByRolloutAndRolloutGroupParent(final Long rolloutId, final Long rolloutGroupParentId, final int limit) { @@ -630,41 +641,51 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl return action; } - private JpaAction startScheduledActionIfNoCancelationHasToBeHandledFirst(final JpaAction action) { - // check if we need to override running update actions - final List overrideObsoleteUpdateActions; + private List startScheduledActionsAndHandleOpenCancellationFirst(final List actions) { + if (!isMultiAssignmentsEnabled()) { + closeOrCancelOpenDeviceActions(actions); + } + final List savedActions = activateActions(actions); + setInitialActionStatus(savedActions); + setAssignmentOnTargets(savedActions); + return Collections.unmodifiableList(savedActions); + } - if (isMultiAssignmentsEnabled()) { - overrideObsoleteUpdateActions = Collections.emptyList(); + private void closeOrCancelOpenDeviceActions(final List actions){ + final List targetIds = actions.stream().map(JpaAction::getTarget).map(Target::getId) + .collect(Collectors.toList()); + if (isActionsAutocloseEnabled()) { + onlineDsAssignmentStrategy.closeObsoleteUpdateActions(targetIds); } else { - final List targetId = Collections.singletonList(action.getTarget().getId()); - if (isActionsAutocloseEnabled()) { - overrideObsoleteUpdateActions = Collections.emptyList(); - onlineDsAssignmentStrategy.closeObsoleteUpdateActions(targetId); - } else { - overrideObsoleteUpdateActions = onlineDsAssignmentStrategy.overrideObsoleteUpdateActions(targetId); - } + onlineDsAssignmentStrategy.overrideObsoleteUpdateActions(targetIds); } + } - action.setActive(true); - action.setStatus(Status.RUNNING); - final JpaAction savedAction = actionRepository.save(action); + private List activateActions(final List actions){ + actions.forEach(action -> { + action.setActive(true); + action.setStatus(Status.RUNNING); + }); + return actionRepository.saveAll(actions); + } - actionStatusRepository.save(onlineDsAssignmentStrategy.createActionStatus(savedAction, null)); + private void setAssignmentOnTargets(final List actions) { + final List assignedDsTargets = actions.stream().map(savedAction -> { + final JpaTarget mergedTarget = (JpaTarget) entityManager.merge(savedAction.getTarget()); + mergedTarget.setAssignedDistributionSet(savedAction.getDistributionSet()); + mergedTarget.setUpdateStatus(TargetUpdateStatus.PENDING); + return mergedTarget; + }).collect(Collectors.toList()); - final JpaTarget target = (JpaTarget) entityManager.merge(savedAction.getTarget()); + targetRepository.saveAll(assignedDsTargets); + } - target.setAssignedDistributionSet(savedAction.getDistributionSet()); - target.setUpdateStatus(TargetUpdateStatus.PENDING); - targetRepository.save(target); - - // in case we canceled an action before for this target, then don't fire - // assignment event - if (overrideObsoleteUpdateActions.contains(savedAction.getId())) { - return null; + private void setInitialActionStatus(final List actions) { + final List statusList = new ArrayList<>(); + for (final JpaAction action : actions) { + statusList.add(onlineDsAssignmentStrategy.createActionStatus(action, null)); } - - return savedAction; + actionStatusRepository.saveAll(statusList); } private void setSkipActionStatus(final JpaAction action) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java index 9130b713f..cc6566a7f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java @@ -51,7 +51,7 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { } @Override - void sendTargetUpdatedEvents(final DistributionSet set, final List targets) { + public void sendTargetUpdatedEvents(final DistributionSet set, final List targets) { targets.forEach(target -> { target.setUpdateStatus(TargetUpdateStatus.IN_SYNC); sendTargetUpdatedEvent(target); @@ -78,19 +78,19 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { } @Override - void closeActiveActions(final List> targetIds) { + public void closeActiveActions(final List> targetIds) { // Not supported by offline case } @Override - void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet set, final List> targetIds, + public void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet set, final List> targetIds, final String currentUser) { targetIds.forEach(tIds -> targetRepository.setAssignedAndInstalledDistributionSetAndUpdateStatus( TargetUpdateStatus.IN_SYNC, set, System.currentTimeMillis(), currentUser, tIds)); } @Override - protected JpaAction createTargetAction(final String initiatedBy, final TargetWithActionType targetWithActionType, + public JpaAction createTargetAction(final String initiatedBy, final TargetWithActionType targetWithActionType, final List targets, final JpaDistributionSet set) { final JpaAction result = super.createTargetAction(initiatedBy, targetWithActionType, targets, set); if (result != null) { @@ -101,7 +101,7 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { } @Override - protected JpaActionStatus createActionStatus(final JpaAction action, final String actionMessage) { + public JpaActionStatus createActionStatus(final JpaAction action, final String actionMessage) { final JpaActionStatus result = super.createActionStatus(action, actionMessage); result.setStatus(Status.FINISHED); result.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Action reported as offline deployment"); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java index 1d2d14079..ee4892fc0 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java @@ -54,7 +54,7 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { } @Override - void sendTargetUpdatedEvents(final DistributionSet set, final List targets) { + public void sendTargetUpdatedEvents(final DistributionSet set, final List targets) { targets.forEach(target -> { target.setUpdateStatus(TargetUpdateStatus.PENDING); sendTargetUpdatedEvent(target); @@ -62,7 +62,7 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { } @Override - void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult) { + public void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult) { if (isMultiAssignmentsEnabled()) { sendDeploymentEvents(Collections.singletonList(assignmentResult)); } else { @@ -71,7 +71,7 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { } @Override - void sendDeploymentEvents(final List assignmentResults) { + public void sendDeploymentEvents(final List assignmentResults) { if (isMultiAssignmentsEnabled()) { sendDeploymentEvent(assignmentResults.stream().flatMap(result -> result.getAssignedEntity().stream()) .collect(Collectors.toList())); @@ -80,7 +80,7 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { } } - void sendDeploymentEvents(final long distributionSetId, final List actions) { + public void sendDeploymentEvents(final long distributionSetId, final List actions) { if (isMultiAssignmentsEnabled()) { sendDeploymentEvent(actions); return; @@ -94,7 +94,7 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { } @Override - List findTargetsForAssignment(final List controllerIDs, final long setId) { + public List findTargetsForAssignment(final List controllerIDs, final long setId) { final Function, List> mapper; if (isMultiAssignmentsEnabled()) { mapper = ids -> targetRepository.findAll(TargetSpecifications.hasControllerIdIn(ids)); @@ -107,18 +107,18 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { } @Override - Set cancelActiveActions(final List> targetIds) { + public Set cancelActiveActions(final List> targetIds) { return targetIds.stream().map(this::overrideObsoleteUpdateActions).flatMap(Collection::stream) .collect(Collectors.toSet()); } @Override - void closeActiveActions(final List> targetIds) { + public void closeActiveActions(final List> targetIds) { targetIds.forEach(this::closeObsoleteUpdateActions); } @Override - void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet set, final List> targetIds, + public void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet set, final List> targetIds, final String currentUser) { targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSetAndUpdateStatus(TargetUpdateStatus.PENDING, set, System.currentTimeMillis(), currentUser, tIds)); @@ -126,7 +126,7 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { } @Override - JpaAction createTargetAction(final String initiatedBy, final TargetWithActionType targetWithActionType, + public JpaAction createTargetAction(final String initiatedBy, final TargetWithActionType targetWithActionType, final List targets, final JpaDistributionSet set) { final JpaAction result = super.createTargetAction(initiatedBy, targetWithActionType, targets, set); if (result != null) { @@ -136,7 +136,7 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { } @Override - JpaActionStatus createActionStatus(final JpaAction action, final String actionMessage) { + public JpaActionStatus createActionStatus(final JpaAction action, final String actionMessage) { final JpaActionStatus result = super.createActionStatus(action, actionMessage); result.setStatus(Status.RUNNING); return result; @@ -146,7 +146,7 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { if (isMultiAssignmentsEnabled()) { sendMultiActionCancelEvent(action); } else { - cancelAssignDistributionSetEvent(action.getTarget(), action.getId()); + cancelAssignDistributionSetEvent(action); } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/RemoteTenantAwareEventTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/RemoteTenantAwareEventTest.java index 19041d7bd..a35dec6dd 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/RemoteTenantAwareEventTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/RemoteTenantAwareEventTest.java @@ -119,6 +119,31 @@ public class RemoteTenantAwareEventTest extends AbstractRemoteEventTest { assertTargetAssignDistributionSetEvent(action, remoteEventJackson); } + @Test + @Description("Verifies that a TargetAssignDistributionSetEvent can be properly serialized and deserialized") + public void testCancelTargetAssignmentEvent() { + + final DistributionSet dsA = testdataFactory.createDistributionSet(""); + + final JpaAction generateAction = new JpaAction(); + generateAction.setActionType(ActionType.FORCED); + generateAction.setTarget(testdataFactory.createTarget("Test")); + generateAction.setDistributionSet(dsA); + generateAction.setStatus(Status.RUNNING); + generateAction.setInitiatedBy(tenantAware.getCurrentUsername()); + + final Action action = actionRepository.save(generateAction); + + final CancelTargetAssignmentEvent cancelEvent = new CancelTargetAssignmentEvent(action, + serviceMatcher.getServiceId()); + + final CancelTargetAssignmentEvent remoteEventProtoStuff = createProtoStuffEvent(cancelEvent); + assertCancelTargetAssignmentEvent(action, remoteEventProtoStuff); + + final CancelTargetAssignmentEvent remoteEventJackson = createJacksonEvent(cancelEvent); + assertCancelTargetAssignmentEvent(action, remoteEventJackson); + } + private void assertTargetAssignDistributionSetEvent(final Action action, final TargetAssignDistributionSetEvent underTest) { @@ -128,4 +153,12 @@ public class RemoteTenantAwareEventTest extends AbstractRemoteEventTest { assertThat(actionProperties).isEqualToComparingFieldByField(new ActionProperties(action)); assertThat(underTest.getDistributionSetId()).isEqualTo(action.getDistributionSet().getId()); } + + private void assertCancelTargetAssignmentEvent(final Action action, final CancelTargetAssignmentEvent underTest) { + assertThat(underTest.getActions().size()).isEqualTo(1); + final ActionProperties actionProperties = underTest.getActions().get(action.getTarget().getControllerId()); + assertThat(actionProperties).isNotNull(); + assertThat(actionProperties).isEqualToComparingFieldByField(new ActionProperties(action)); + assertThat(underTest.getActionPropertiesForController(action.getTarget().getControllerId())).isPresent(); + } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetEventTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetEventTest.java index e9b7f247f..45f1d3189 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetEventTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/event/remote/entity/TargetEventTest.java @@ -36,17 +36,6 @@ public class TargetEventTest extends AbstractRemoteEntityEventTest { assertAndCreateRemoteEvent(TargetUpdatedEvent.class); } - @Test - @Description("Verifies that cancel target assignment event works") - public void testCancelTargetAssignmentEvent() { - final Target target = createEntity(); - final CancelTargetAssignmentEvent assignmentEvent = new CancelTargetAssignmentEvent(target, 1L, "node"); - final CancelTargetAssignmentEvent underTest = (CancelTargetAssignmentEvent) assertEntity(target, - assignmentEvent); - - assertThat(underTest.getActionId()).isNotNull(); - } - @Override protected Target createEntity() { return testdataFactory.createTarget("12345"); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java index 29ae9b64c..3ce29d8b9 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java @@ -44,7 +44,7 @@ import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent; 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.CancelTargetAssignmentEvent; +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.SoftwareModuleCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java index 01a503791..7de902710 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java @@ -32,7 +32,7 @@ import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent; 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.ActionUpdatedEvent; -import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent; +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; @@ -265,7 +265,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = TargetUpdatedEvent.class, count = 40), @Expect(type = TargetAssignDistributionSetEvent.class, count = 2), @Expect(type = ActionCreatedEvent.class, count = 40), - @Expect(type = CancelTargetAssignmentEvent.class, count = 20), + @Expect(type = CancelTargetAssignmentEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 20), @Expect(type = DistributionSetCreatedEvent.class, count = 2), @Expect(type = SoftwareModuleCreatedEvent.class, count = 6) }) @@ -945,11 +945,11 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = TargetUpdatedEvent.class, count = 3 * 4), @Expect(type = ActionCreatedEvent.class, count = 3 * 4), @Expect(type = ActionUpdatedEvent.class, count = 4 * 2), - @Expect(type = CancelTargetAssignmentEvent.class, count = 4 * 2), + @Expect(type = CancelTargetAssignmentEvent.class, count = 2), @Expect(type = DistributionSetCreatedEvent.class, count = 3), @Expect(type = SoftwareModuleCreatedEvent.class, count = 9), @Expect(type = TargetAssignDistributionSetEvent.class, count = 3) }) - void multipleDeployments() throws InterruptedException { + void multipleDeployments() { final String undeployedTargetPrefix = "undep-T"; final int noOfUndeployedTargets = 5; diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java index 9e7275257..30892eebe 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java +++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/DdiApiModelProperties.java @@ -131,7 +131,8 @@ final class DdiApiModelProperties { static final String ACTION_HISTORY_RESP_STATUS = "Status of the deployment based on previous feedback by the device."; - static final String ACTION_HISTORY_RESP_MESSAGES = "Messages are previously sent to the feedback channel in LIFO order by the device."; + static final String ACTION_HISTORY_RESP_MESSAGES = "Messages are previously sent to the feedback channel in LIFO order by the device. " + + "Note: The first status message is set by the system and describes the trigger of the deployment"; static final String UPDATE_MODE = "Optional parameter to specify the update mode that should be applied when updating target attributes. " + "Valid values are 'merge', 'replace', and 'remove'. Defaults to 'merge'."; diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java index 33dc540e5..8bdb1e7f2 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java +++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java @@ -382,7 +382,11 @@ public class RootControllerDocumentationTest extends AbstractApiRestDocumentatio fieldWithPath("deployment.chunks[].part").description(DdiApiModelProperties.CHUNK_TYPE), fieldWithPath("deployment.chunks[].name").description(DdiApiModelProperties.CHUNK_NAME), fieldWithPath("deployment.chunks[].version") - .description(DdiApiModelProperties.CHUNK_VERSION)))); + .description(DdiApiModelProperties.CHUNK_VERSION), + fieldWithPath("actionHistory.status") + .description(DdiApiModelProperties.ACTION_HISTORY_RESP_STATUS), + fieldWithPath("actionHistory.messages") + .description(DdiApiModelProperties.ACTION_HISTORY_RESP_MESSAGES)))); }