From e82b9cee70406e6a7d826cca563300e9d536c590 Mon Sep 17 00:00:00 2001 From: Michael Herdt <55577866+herdt-michael@users.noreply.github.com> Date: Fri, 25 Sep 2020 13:12:42 +0200 Subject: [PATCH] Feature/type for multi actions (#986) * Introduce different types of Multi Action Event to distinguish between an Assignment and a Cancel. * Minimize the payload for the multiAction assignment and cancel event. Write tests for the MultiActionCancelEvent. * Remove unused action status. * Move list of actionIds to MultiActionEvent and declare it as abstract. * Remove unused imports. Signed-off-by: Michael Herdt --- .../amqp/AmqpMessageDispatcherService.java | 55 +++++++++-------- ...ssageDispatcherServiceIntegrationTest.java | 55 ++++++++--------- .../event/remote/MultiActionAssignEvent.java | 47 +++++++++++++++ .../event/remote/MultiActionCancelEvent.java | 47 +++++++++++++++ .../event/remote/MultiActionEvent.java | 34 ++++++++--- .../org/eclipse/hawkbit/event/EventType.java | 6 +- .../jpa/OnlineDsAssignmentStrategy.java | 40 +++++++++---- .../remote/RemoteTenantAwareEventTest.java | 49 +++++++++++---- .../jpa/DeploymentManagementTest.java | 60 ++++++++++++++----- 9 files changed, 291 insertions(+), 102 deletions(-) create mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionAssignEvent.java create mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionCancelEvent.java 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 6ab11ec04..0d150c17b 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 @@ -8,17 +8,6 @@ */ package org.eclipse.hawkbit.amqp; -import static org.eclipse.hawkbit.repository.RepositoryConstants.MAX_ACTION_COUNT; - -import java.net.URI; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.function.Function; -import java.util.stream.Collectors; - import org.eclipse.hawkbit.api.ApiType; import org.eclipse.hawkbit.api.ArtifactUrl; import org.eclipse.hawkbit.api.ArtifactUrlHandler; @@ -66,6 +55,17 @@ import org.springframework.context.event.EventListener; import org.springframework.data.domain.PageRequest; import org.springframework.util.CollectionUtils; +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static org.eclipse.hawkbit.repository.RepositoryConstants.MAX_ACTION_COUNT; + /** * {@link AmqpMessageDispatcherService} create all outgoing AMQP messages and * delegate the messages to a {@link AmqpMessageSenderService}. @@ -104,8 +104,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { * @param targetManagement * to access target information * @param serviceMatcher - * to check in cluster case if the message is from the same - * cluster node + * to check in cluster case if the message is from the same cluster + * node * @param distributionSetManagement * to retrieve modules */ @@ -128,8 +128,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { } /** - * Method to send a message to a RabbitMQ Exchange after the Distribution - * set has been assign to a Target. + * Method to send a message to a RabbitMQ Exchange after the Distribution set + * has been assign to a Target. * * @param assignedEvent * the object to be send. @@ -157,7 +157,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { /** * Listener for Multi-Action events. * - * @param e + * @param multiActionEvent * the Multi-Action event to be processed */ @EventListener(classes = MultiActionEvent.class) @@ -242,9 +242,9 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { /** * Method to get the type of event depending on whether the action is a - * DOWNLOAD_ONLY action or if it has a valid maintenance window available or - * not based on defined maintenance schedule. In case of no maintenance - * schedule or if there is a valid window available, the topic + * DOWNLOAD_ONLY action or if it has a valid maintenance window available or not + * based on defined maintenance schedule. In case of no maintenance schedule or + * if there is a valid window available, the topic * {@link EventTopic#DOWNLOAD_AND_INSTALL} is returned else * {@link EventTopic#DOWNLOAD} is returned. * @@ -260,8 +260,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { } /** - * Determines the {@link EventTopic} for the given {@link Action}, depending - * on its action type. + * Determines the {@link EventTopic} for the given {@link Action}, depending on + * its action type. * * @param action * to obtain the corresponding {@link EventTopic} for @@ -276,8 +276,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { } /** - * Method to send a message to a RabbitMQ Exchange after the assignment of - * the Distribution set to a Target has been canceled. + * Method to send a message to a RabbitMQ Exchange after the assignment of the + * Distribution set to a Target has been canceled. * * @param cancelEvent * that is to be converted to a DMF message @@ -300,12 +300,11 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { } /** - * Method to send a message to a RabbitMQ Exchange after a Target was - * deleted. + * Method to send a message to a RabbitMQ Exchange after a Target was deleted. * * @param deleteEvent - * the TargetDeletedEvent which holds the necessary data for - * sending a target delete message. + * the TargetDeletedEvent which holds the necessary data for sending + * a target delete message. */ @EventListener(classes = TargetDeletedEvent.class) protected void targetDelete(final TargetDeletedEvent deleteEvent) { @@ -488,4 +487,4 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT), module.getId()).getContent(); } -} \ No newline at end of file +} 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 e807b9baa..3c1442e23 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 @@ -8,23 +8,9 @@ */ package org.eclipse.hawkbit.integration; -import static org.assertj.core.api.Assertions.assertThat; -import static org.eclipse.hawkbit.dmf.amqp.api.EventTopic.DOWNLOAD; -import static org.eclipse.hawkbit.dmf.amqp.api.MessageType.EVENT; -import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ONLY; - -import java.util.AbstractMap.SimpleEntry; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; -import java.util.concurrent.Callable; -import java.util.stream.Collectors; - +import io.qameta.allure.Description; +import io.qameta.allure.Feature; +import io.qameta.allure.Story; import org.eclipse.hawkbit.dmf.amqp.api.EventTopic; import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey; import org.eclipse.hawkbit.dmf.json.model.DmfActionRequest; @@ -34,7 +20,8 @@ import org.eclipse.hawkbit.dmf.json.model.DmfMultiActionRequest; import org.eclipse.hawkbit.dmf.json.model.DmfMultiActionRequest.DmfMultiActionElement; import org.eclipse.hawkbit.dmf.json.model.DmfSoftwareModule; import org.eclipse.hawkbit.repository.DeploymentManagement; -import org.eclipse.hawkbit.repository.event.remote.MultiActionEvent; +import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent; +import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent; @@ -66,9 +53,22 @@ import org.junit.Test; import org.mockito.Mockito; import org.springframework.amqp.core.Message; -import io.qameta.allure.Description; -import io.qameta.allure.Feature; -import io.qameta.allure.Story; +import java.util.AbstractMap.SimpleEntry; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.hawkbit.dmf.amqp.api.EventTopic.DOWNLOAD; +import static org.eclipse.hawkbit.dmf.amqp.api.MessageType.EVENT; +import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ONLY; @Feature("Component Tests - Device Management Federation API") @Story("Amqp Message Dispatcher Service") @@ -164,7 +164,7 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @Test @Description("If multi assignment is enabled multi-action messages are sent.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), - @Expect(type = MultiActionEvent.class, count = 2), + @Expect(type = MultiActionAssignEvent.class, count = 2), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = CancelTargetAssignmentEvent.class, count = 0), @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 0), @@ -247,7 +247,8 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @Test @Description("Handle cancelation process of an action in multi assignment mode.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), - @Expect(type = MultiActionEvent.class, count = 3), + @Expect(type = MultiActionCancelEvent.class, count = 1), + @Expect(type = MultiActionAssignEvent.class, count = 2), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = CancelTargetAssignmentEvent.class, count = 0), @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 2), @@ -278,7 +279,7 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @Test @Description("Handle finishing an action in multi assignment mode.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), - @Expect(type = MultiActionEvent.class, count = 2), + @Expect(type = MultiActionAssignEvent.class, count = 2), @Expect(type = TargetAttributesRequestedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = CancelTargetAssignmentEvent.class, count = 0), @@ -305,7 +306,7 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @Test @Description("If multi assignment is enabled assigning a DS multiple times creates a new action every time.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), - @Expect(type = MultiActionEvent.class, count = 2), + @Expect(type = MultiActionAssignEvent.class, count = 2), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = CancelTargetAssignmentEvent.class, count = 0), @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 0), @@ -349,7 +350,7 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @Test @Description("If multi assignment is enabled multiple rollouts with the same DS lead to multiple actions.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), - @Expect(type = MultiActionEvent.class, count = 2), + @Expect(type = MultiActionAssignEvent.class, count = 2), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = CancelTargetAssignmentEvent.class, count = 0), @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 2), @@ -380,7 +381,7 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer @Test @Description("If multi assignment is enabled finishing one rollout does not affect other rollouts of the target.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), - @Expect(type = MultiActionEvent.class, count = 3), @Expect(type = ActionCreatedEvent.class, count = 3), + @Expect(type = MultiActionAssignEvent.class, count = 3), @Expect(type = ActionCreatedEvent.class, count = 3), @Expect(type = ActionUpdatedEvent.class, count = 5), @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 2), diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionAssignEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionAssignEvent.java new file mode 100644 index 000000000..5411773dd --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionAssignEvent.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2020 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 java.util.List; + +/** + * Generic deployment event for the Multi-Assignments feature. The event extends + * the {@link MultiActionEvent} and holds a list of controller IDs to identify + * the targets which are affected by a deployment action and a list of + * actionIds containing the identifiers of the affected actions + * as payload. This event is only published in case of an assignment. + */ +public class MultiActionAssignEvent extends MultiActionEvent { + + private static final long serialVersionUID = 1L; + + /** + * Default constructor. + */ + public MultiActionAssignEvent() { + // for serialization libs like jackson + } + + /** + * Constructor. + * + * @param tenant + * tenant the event is scoped to + * @param applicationId + * the application id + * @param actions + * the actions of the deployment action + */ + public MultiActionAssignEvent(String tenant, String applicationId, List actions) { + super(tenant, applicationId, actions); + } + +} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionCancelEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionCancelEvent.java new file mode 100644 index 000000000..c8e1a26bf --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionCancelEvent.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2020 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; + +/** + * Generic deployment event for the Multi-Assignments feature. The event extends + * the {@link MultiActionEvent} and holds a list of controller IDs to identify + * the targets which are affected by a deployment action and a list of + * actionIds containing the identifiers of the affected actions + * as payload. This event is only published in case of an cancellation. + */ +public class MultiActionCancelEvent extends MultiActionEvent { + + private static final long serialVersionUID = 1L; + + /** + * Default constructor. + */ + public MultiActionCancelEvent() { + // for serialization libs like jackson + } + + /** + * Constructor. + * + * @param tenant + * tenant the event is scoped to + * @param applicationId + * the application id + * @param actions + * the actions to be canceled + */ + public MultiActionCancelEvent(String tenant, String applicationId, List actions) { + super(tenant, applicationId, actions); + } + +} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionEvent.java index e049d0357..873efe7e4 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionEvent.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionEvent.java @@ -8,9 +8,14 @@ */ package org.eclipse.hawkbit.repository.event.remote; +import org.eclipse.hawkbit.repository.model.Action; +import org.eclipse.hawkbit.repository.model.Target; +import org.springframework.hateoas.Identifiable; + import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.stream.Collectors; /** * Generic deployment event for the Multi-Assignments feature. The event payload @@ -18,11 +23,12 @@ import java.util.List; * a deployment action (e.g. a software assignment (update) or a cancellation of * an update). */ -public class MultiActionEvent extends RemoteTenantAwareEvent implements Iterable { +public abstract class MultiActionEvent extends RemoteTenantAwareEvent implements Iterable { private static final long serialVersionUID = 1L; private final List controllerIds = new ArrayList<>(); + private final List actionIds = new ArrayList<>(); /** * Default constructor. @@ -33,17 +39,18 @@ public class MultiActionEvent extends RemoteTenantAwareEvent implements Iterable /** * Constructor. - * + * * @param tenant * tenant the event is scoped to * @param applicationId * the application id - * @param controllerIds - * the controller IDs of the affected targets + * @param actions + * the actions involved */ - public MultiActionEvent(final String tenant, final String applicationId, final List controllerIds) { + public MultiActionEvent(String tenant, String applicationId, List actions) { super(applicationId, tenant, applicationId); - this.controllerIds.addAll(controllerIds); + this.controllerIds.addAll(getControllerIdsFromActions(actions)); + this.actionIds.addAll(getIdsFromActions(actions)); } public List getControllerIds() { @@ -55,4 +62,17 @@ public class MultiActionEvent extends RemoteTenantAwareEvent implements Iterable return controllerIds.iterator(); } -} \ No newline at end of file + public List getActionIds() { + return actionIds; + } + + private static List getControllerIdsFromActions(final List actions) { + return actions.stream().map(Action::getTarget).map(Target::getControllerId).distinct() + .collect(Collectors.toList()); + } + + private static List getIdsFromActions(final List actions) { + return actions.stream().map(Identifiable::getId).collect(Collectors.toList()); + } + +} 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 49fba4bcb..17c230410 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 @@ -17,7 +17,8 @@ import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.DistributionSetTagDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.DistributionSetTypeDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent; -import org.eclipse.hawkbit.repository.event.remote.MultiActionEvent; +import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent; +import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent; import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.RolloutGroupDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleDeletedEvent; @@ -135,7 +136,8 @@ public class EventType { TYPES.put(37, TargetAttributesRequestedEvent.class); // deployment event for assignments and /or cancellations - TYPES.put(38, MultiActionEvent.class); + TYPES.put(38, MultiActionAssignEvent.class); + TYPES.put(39, MultiActionCancelEvent.class); } private int value; 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 9086aeca3..f008084d5 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 @@ -18,7 +18,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.eclipse.hawkbit.repository.QuotaManagement; -import org.eclipse.hawkbit.repository.event.remote.MultiActionEvent; +import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent; +import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; @@ -144,14 +145,14 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { void cancelAssignment(final JpaAction action) { if (isMultiAssignmentsEnabled()) { - sendMultiActionEvent(action.getTarget()); + sendMultiActionCancelEvent(action); } else { cancelAssignDistributionSetEvent(action.getTarget(), action.getId()); } } - private void sendMultiActionEvent(final Target target) { - sendMultiActionEvent(target.getTenant(), Collections.singletonList(target.getControllerId())); + private void sendMultiActionCancelEvent(final Action action) { + sendMultiActionCancelEvent(action.getTenant(), Collections.singletonList(action)); } private void sendDeploymentEvent(final List actions) { @@ -160,8 +161,7 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { return; } final String tenant = filteredActions.get(0).getTenant(); - sendMultiActionEvent(tenant, filteredActions.stream().map(action -> action.getTarget().getControllerId()) - .collect(Collectors.toList())); + sendMultiActionAssignEvent(tenant, filteredActions); } private DistributionSetAssignmentResult sendDistributionSetAssignedEvent( @@ -190,17 +190,31 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { } /** - * Helper to fire a {@link MultiActionEvent}. This method may only be called - * if the Multi-Assignments feature is enabled. - * + * Helper to fire a {@link MultiActionCancelEvent}. This method may only be + * called if the Multi-Assignments feature is enabled. + * * @param tenant * the event is scoped to - * @param controllerIds - * of the targets the event refers to + * @param actions + * assigned to the targets */ - private void sendMultiActionEvent(final String tenant, final List controllerIds) { + private void sendMultiActionCancelEvent(final String tenant, final List actions) { afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher() - .publishEvent(new MultiActionEvent(tenant, eventPublisherHolder.getApplicationId(), controllerIds))); + .publishEvent(new MultiActionCancelEvent(tenant, eventPublisherHolder.getApplicationId(), actions))); + } + + /** + * Helper to fire a {@link MultiActionAssignEvent}. This method may only be + * called if the Multi-Assignments feature is enabled. + * + * @param tenant + * the event is scoped to + * @param actions + * assigned to the targets + */ + private void sendMultiActionAssignEvent(final String tenant, final List actions) { + afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher() + .publishEvent(new MultiActionAssignEvent(tenant, eventPublisherHolder.getApplicationId(), actions))); } private static Stream filterCancellations(final List actions) { 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 e94c3e2b7..e622e8afe 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 @@ -12,6 +12,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.model.Action; @@ -33,22 +34,50 @@ public class RemoteTenantAwareEventTest extends AbstractRemoteEventTest { private static final String APPLICATION_ID_DEFAULT = "Node"; - @Test - @Description("Verifies that a MultiActionEvent can be properly serialized and deserialized") - public void testMultiActionEvent() { + private Action createAction(final String controllerId) { + final JpaAction generateAction = new JpaAction(); + generateAction.setActionType(ActionType.FORCED); + generateAction.setTarget(testdataFactory.createTarget(controllerId)); + generateAction.setStatus(Status.RUNNING); + return generateAction; + } + @Test + @Description("Verifies that a testMultiActionAssignEvent can be properly serialized and deserialized") + public void testMultiActionAssignEvent() { final List controllerIds = Arrays.asList("id0", "id1", "id2", "id3", "id4loooooooooooooooooooooooooooooooooooonnnnnnnnnnnnnnnnnng"); - final MultiActionEvent event = new MultiActionEvent(TENANT_DEFAULT, APPLICATION_ID_DEFAULT, controllerIds); + final List actions = controllerIds.stream().map(this::createAction).collect(Collectors.toList()); - final MultiActionEvent remoteEventProtoStuff = createProtoStuffEvent(event); - assertThat(event).isEqualTo(remoteEventProtoStuff); - assertThat(remoteEventProtoStuff.getControllerIds()).containsExactlyElementsOf(controllerIds); + final MultiActionAssignEvent assignEvent = new MultiActionAssignEvent(TENANT_DEFAULT, APPLICATION_ID_DEFAULT, + actions); - final MultiActionEvent remoteEventJackson = createJacksonEvent(event); - assertThat(event).isEqualTo(remoteEventJackson); - assertThat(remoteEventJackson.getControllerIds()).containsExactlyElementsOf(controllerIds); + final MultiActionAssignEvent remoteAssignEventProtoStuff = createProtoStuffEvent(assignEvent); + assertThat(assignEvent).isEqualTo(remoteAssignEventProtoStuff); + assertThat(remoteAssignEventProtoStuff.getControllerIds()).containsExactlyElementsOf(controllerIds); + final MultiActionAssignEvent remoteAssignEventJackson = createJacksonEvent(assignEvent); + assertThat(assignEvent).isEqualTo(remoteAssignEventJackson); + assertThat(remoteAssignEventJackson.getControllerIds()).containsExactlyElementsOf(controllerIds); + } + + @Test + @Description("Verifies that a MultiActionCancelEvent can be properly serialized and deserialized") + public void testMultiActionCancelEvent() { + final List controllerIds = Arrays.asList("id0", "id1", "id2", "id3", + "id4loooooooooooooooooooooooooooooooooooonnnnnnnnnnnnnnnnnng"); + final List actions = controllerIds.stream().map(this::createAction).collect(Collectors.toList()); + + final MultiActionCancelEvent cancelEvent = new MultiActionCancelEvent(TENANT_DEFAULT, APPLICATION_ID_DEFAULT, + actions); + + final MultiActionCancelEvent remoteCancelEventProtoStuff = createProtoStuffEvent(cancelEvent); + assertThat(cancelEvent).isEqualTo(remoteCancelEventProtoStuff); + assertThat(remoteCancelEventProtoStuff.getControllerIds()).containsExactlyElementsOf(controllerIds); + + final MultiActionCancelEvent remoteCancelEventJackson = createJacksonEvent(cancelEvent); + assertThat(cancelEvent).isEqualTo(remoteCancelEventJackson); + assertThat(remoteCancelEventJackson.getControllerIds()).containsExactlyElementsOf(controllerIds); } @Test 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 d92f610be..90bcc5e6f 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 @@ -27,7 +27,8 @@ import javax.validation.ConstraintViolationException; import org.assertj.core.api.Assertions; import org.eclipse.hawkbit.repository.ActionStatusFields; import org.eclipse.hawkbit.repository.DeploymentManagement; -import org.eclipse.hawkbit.repository.event.remote.MultiActionEvent; +import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent; +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; @@ -561,7 +562,8 @@ public 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 = MultiActionEvent.class, count = 2), + @Expect(type = MultiActionAssignEvent.class, count = 2), + @Expect(type = MultiActionCancelEvent.class, count = 0), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0) }) public void previousAssignmentsAreNotCanceledInMultiAssignMode() { enableMultiAssignments(); @@ -600,7 +602,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = TargetUpdatedEvent.class, count = 4), @Expect(type = ActionCreatedEvent.class, count = 4), @Expect(type = DistributionSetCreatedEvent.class, count = 2), @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), - @Expect(type = MultiActionEvent.class, count = 1), + @Expect(type = MultiActionAssignEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0) }) public void multiassignmentInOneRequest() { final List targets = testdataFactory.createTargets(2); @@ -620,6 +622,36 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { }); } + @Test + @Description("Assign multiple DSs to multiple Targets in one request in multiAssignment mode and cancel each created action afterwards.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 2), + @Expect(type = TargetUpdatedEvent.class, count = 4), @Expect(type = ActionCreatedEvent.class, count = 4), + @Expect(type = DistributionSetCreatedEvent.class, count = 2), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = MultiActionAssignEvent.class, count = 1), + @Expect(type = MultiActionCancelEvent.class, count = 4), + @Expect(type = ActionUpdatedEvent.class, count = 4), + @Expect(type = TargetAssignDistributionSetEvent.class, count = 0) }) + public void cancelMultiAssignmentActions() { + final List targets = testdataFactory.createTargets(2); + final List distributionSets = testdataFactory.createDistributionSets(2); + final List deploymentRequests = createAssignmentRequests(distributionSets, targets, 34); + + enableMultiAssignments(); + final List results = deploymentManagement + .assignDistributionSets(deploymentRequests); + + assertThat(getResultingActionCount(results)).isEqualTo(deploymentRequests.size()); + + final List dsIds = distributionSets.stream().map(DistributionSet::getId).collect(Collectors.toList()); + targets.forEach(target -> { + actionRepository.findByTargetControllerId(PAGE, target.getControllerId()).forEach(action -> { + assertThat(action.getDistributionSet().getId()).isIn(dsIds); + deploymentManagement.cancelAction(action.getId()); + }); + }); + } + protected List createAssignmentRequests(final Collection distributionSets, final Collection targets, final int weight) { final List deploymentRequests = new ArrayList<>(); @@ -689,7 +721,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 3), @Expect(type = TargetUpdatedEvent.class, count = 2), - @Expect(type = MultiActionEvent.class, count = 1) }) + @Expect(type = MultiActionAssignEvent.class, count = 1) }) public void duplicateAssignmentsInRequestAreOnlyRemovedIfMultiassignmentDisabled() { final String targetId = testdataFactory.createTarget().getControllerId(); final Long dsId = testdataFactory.createDistributionSet().getId(); @@ -767,7 +799,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { @Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = SoftwareModuleCreatedEvent.class, count = 3), @Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2), - @Expect(type = MultiActionEvent.class, count = 2) }) + @Expect(type = MultiActionAssignEvent.class, count = 2) }) public void weightValidatedAndSaved() { final String targetId = testdataFactory.createTarget().getControllerId(); final Long dsId = testdataFactory.createDistributionSet().getId(); @@ -794,8 +826,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { /** * test a simple deployment by calling the - * {@link TargetRepository#assignDistributionSet(DistributionSet, Iterable)} - * and checking the active action and the action history of the targets. + * {@link TargetRepository#assignDistributionSet(DistributionSet, Iterable)} and + * checking the active action and the action history of the targets. * */ @Test @@ -1035,10 +1067,9 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { } /** - * test the deletion of {@link DistributionSet}s including exception in case - * of {@link Target}s are assigned by - * {@link Target#getAssignedDistributionSet()} or - * {@link Target#getInstalledDistributionSet()} + * test the deletion of {@link DistributionSet}s including exception in case of + * {@link Target}s are assigned by {@link Target#getAssignedDistributionSet()} + * or {@link Target#getInstalledDistributionSet()} */ @Test @Description("Deletes distribution set. Expected behaviour is that a soft delete is performed " @@ -1306,8 +1337,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { * Helper methods that creates 2 lists of targets and a list of distribution * sets. *

- * All created distribution sets are assigned to all targets of the - * target list deployedTargets. + * All created distribution sets are assigned to all targets of the target + * list deployedTargets. * * @param undeployedTargetPrefix * prefix to be used as target controller prefix @@ -1316,8 +1347,7 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest { * @param deployedTargetPrefix * prefix to be used as target controller prefix * @param noOfDeployedTargets - * number of targets to which the created distribution sets - * assigned + * number of targets to which the created distribution sets assigned * @param noOfDistributionSets * number of distribution sets * @param distributionSetPrefix