Support for Multi-Assignments (#833)

* First draft of new assignment logic

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Enhancements of System Configuration view

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Drag&drop enhancements for multiple distribution set assignments

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Misc fixes

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* test that previous assignments are not canceled

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* add description and expected events to test

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* extend TenantConfigurationManagement by NullPointerException proof
getConfigurationValue method

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* Hide "Required Migration Step" if Multi Assignments is enabled

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* Make fields transient

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* Add IDs for Required Migration Step elements

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* Save work in progress

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Added new DMF message DmfMultiActionRequest

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* DMF enhancements to send out MultiActionRequest messages

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Minor changes

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Multi Assignment support for cancellations

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* fix permission problems and immutable lists

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* add message dispatcher tests for outgoing multiassignment messages, fix
old tests

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* Implement Multi-Assignment support for rollout groups

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Minor changes

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Refactoring

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Register new deployment event with protobuff framework

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Allow same DS to be assigned multiple times

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix assignment with pending cancellations

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Reduce repository /DB calls

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Revert latest perf fix (causing a regression)

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* test if a rollout sends multiaction messages in multiassignment mode

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* Minor changes

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Do not close new action if DS is already assigned (if multi-assign on)

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* add test that starts and finishes multiple rollouts in multiassignt mode

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* add javadoc to test method

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* Prevent Multi-Assignments from being disabled via Repo Config UI

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Add link to Provisioning State Machine

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* test that Multiassignment can not be disabled via mgmt-api

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* refactor AmqpMessageHandlerService code

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* Prevent Multi-Assignments feature from being disabled via Mgmt REST API

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* add license header, remove unused instance variables

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* fix tenantConfigurationManagement mock in test

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* return empty list instead of null

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* add ddi test for multiassignment, fix old test

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* Prevent autoclose from being modified if Multi-Assignments is enabled

# WARNING: head commit changed in the meantime

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Add test for autoClose /multiAssign

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Javadoc improvements

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* change test method that waits for dmf messages to be dispatched

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* clean up code

Signed-off-by: Stefan Klotz <stefan.klotz@bosch-si.com>

* Fix UI-related PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings, Sonar issues, and test failures

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix Sonar findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>

* Fix PR review findings

Signed-off-by: Stefan Behl <stefan.behl@bosch-si.com>
This commit is contained in:
Stefan Behl
2019-05-16 10:30:40 +02:00
committed by Dominic Schabel
parent f16f986eed
commit 1ac40a7a95
65 changed files with 1906 additions and 588 deletions

View File

@@ -16,14 +16,10 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.File;
import java.net.URL;
import java.util.Optional;
import org.eclipse.hawkbit.api.HostnameResolver;
import org.eclipse.hawkbit.artifact.repository.ArtifactFilesystem;
import org.eclipse.hawkbit.artifact.repository.model.AbstractDbArtifact;
import org.eclipse.hawkbit.artifact.repository.model.DbArtifactHash;
import org.eclipse.hawkbit.cache.DownloadIdCache;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
@@ -109,7 +105,7 @@ public class AmqpControllerAuthenticationTest {
private ControllerManagement controllerManagementMock;
@Mock
private Target targteMock;
private Target targetMock;
private static final TenantConfigurationValue<Boolean> CONFIG_VALUE_FALSE = TenantConfigurationValue
.<Boolean> builder().value(Boolean.FALSE).build();
@@ -137,11 +133,11 @@ public class AmqpControllerAuthenticationTest {
.thenReturn(CONFIG_VALUE_FALSE);
final ControllerManagement controllerManagement = mock(ControllerManagement.class);
when(controllerManagement.getByControllerId(anyString())).thenReturn(Optional.of(targteMock));
when(controllerManagement.get(any(Long.class))).thenReturn(Optional.of(targteMock));
when(controllerManagement.getByControllerId(anyString())).thenReturn(Optional.of(targetMock));
when(controllerManagement.get(any(Long.class))).thenReturn(Optional.of(targetMock));
when(targteMock.getSecurityToken()).thenReturn(CONTROLLER_ID);
when(targteMock.getControllerId()).thenReturn(CONTROLLER_ID);
when(targetMock.getSecurityToken()).thenReturn(CONTROLLER_ID);
when(targetMock.getControllerId()).thenReturn(CONTROLLER_ID);
final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware();
final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware);
@@ -162,11 +158,9 @@ public class AmqpControllerAuthenticationTest {
when(artifactManagementMock.get(ARTIFACT_ID)).thenReturn(Optional.of(testArtifact));
when(artifactManagementMock.findFirstBySHA1(SHA1)).thenReturn(Optional.of(testArtifact));
final AbstractDbArtifact artifact = new ArtifactFilesystem(new File("does not exist"), SHA1,
new DbArtifactHash(SHA1, "md5 test"), ARTIFACT_SIZE, null);
amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate,
mock(AmqpMessageDispatcherService.class), controllerManagementMock, new JpaEntityFactory());
mock(AmqpMessageDispatcherService.class), controllerManagementMock, new JpaEntityFactory(),
systemSecurityContext, tenantConfigurationManagementMock);
amqpAuthenticationMessageHandlerService = new AmqpAuthenticationMessageHandler(rabbitTemplate,
authenticationManager, artifactManagementMock, cacheMock, hostnameResolverMock,

View File

@@ -13,7 +13,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
@@ -101,7 +100,7 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
senderService = Mockito.mock(DefaultAmqpMessageSenderService.class);
final ArtifactUrlHandler artifactUrlHandlerMock = Mockito.mock(ArtifactUrlHandler.class);
when(artifactUrlHandlerMock.getUrls(anyObject(), anyObject()))
when(artifactUrlHandlerMock.getUrls(any(), any()))
.thenReturn(Arrays.asList(new ArtifactUrl("http", "download", "http://mockurl")));
systemManagement = Mockito.mock(SystemManagement.class);
@@ -113,7 +112,7 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
amqpMessageDispatcherService = new AmqpMessageDispatcherService(rabbitTemplate, senderService,
artifactUrlHandlerMock, systemSecurityContext, systemManagement, targetManagement, serviceMatcher,
distributionSetManagement, softwareModuleManagement);
distributionSetManagement, softwareModuleManagement, deploymentManagement);
}
@@ -125,7 +124,7 @@ public class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
}
private Action createAction(final DistributionSet testDs) {
return deploymentManagement.findAction(assignDistributionSet(testDs, testTarget).getActions().get(0)).get();
return deploymentManagement.findAction(assignDistributionSet(testDs, testTarget).getActionIds().get(0)).get();
}
@Test

View File

@@ -9,6 +9,7 @@
package org.eclipse.hawkbit.amqp;
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -39,6 +40,7 @@ import org.eclipse.hawkbit.dmf.json.model.DmfUpdateMode;
import org.eclipse.hawkbit.repository.ArtifactManagement;
import org.eclipse.hawkbit.repository.ControllerManagement;
import org.eclipse.hawkbit.repository.EntityFactory;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.UpdateMode;
import org.eclipse.hawkbit.repository.builder.ActionStatusBuilder;
import org.eclipse.hawkbit.repository.builder.ActionStatusCreate;
@@ -49,9 +51,12 @@ import org.eclipse.hawkbit.repository.model.ActionProperties;
import org.eclipse.hawkbit.repository.model.Artifact;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken;
import org.eclipse.hawkbit.security.DmfTenantSecurityToken.FileResource;
import org.eclipse.hawkbit.security.SecurityContextTenantAware;
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.junit.Before;
import org.junit.Test;
@@ -100,6 +105,9 @@ public class AmqpMessageHandlerServiceTest {
@Mock
private ArtifactManagement artifactManagementMock;
@Mock
private TenantConfigurationManagement tenantConfigurationManagement;
@Mock
private AmqpControllerAuthentication authenticationManagerMock;
@@ -128,16 +136,21 @@ public class AmqpMessageHandlerServiceTest {
private ArgumentCaptor<UpdateMode> modeCaptor;
@Before
@SuppressWarnings({ "rawtypes", "unchecked" })
public void before() throws Exception {
messageConverter = new Jackson2JsonMessageConverter();
when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter);
when(artifactManagementMock.findFirstBySHA1(SHA1)).thenReturn(Optional.empty());
final TenantConfigurationValue multiAssignmentConfig = TenantConfigurationValue.builder().value(Boolean.FALSE)
.global(Boolean.FALSE).build();
when(tenantConfigurationManagement.getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class))
.thenReturn(multiAssignmentConfig);
final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware();
final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware);
amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherServiceMock,
controllerManagementMock, entityFactoryMock);
amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherServiceMock,
controllerManagementMock, entityFactoryMock);
controllerManagementMock, entityFactoryMock, systemSecurityContext, tenantConfigurationManagement);
amqpAuthenticationMessageHandlerService = new AmqpAuthenticationMessageHandler(rabbitTemplate,
authenticationManagerMock, artifactManagementMock, downloadIdCache, hostnameResolverMock,
controllerManagementMock, tenantAwareMock);
@@ -465,8 +478,8 @@ public class AmqpMessageHandlerServiceTest {
final ArgumentCaptor<ActionProperties> actionPropertiesCaptor = ArgumentCaptor.forClass(ActionProperties.class);
final ArgumentCaptor<Target> targetCaptor = ArgumentCaptor.forClass(Target.class);
verify(amqpMessageDispatcherServiceMock, times(1))
.sendUpdateMessageToTarget(actionPropertiesCaptor.capture(), targetCaptor.capture(), any(Map.class));
verify(amqpMessageDispatcherServiceMock, times(1)).sendUpdateMessageToTarget(actionPropertiesCaptor.capture(),
targetCaptor.capture(), any(Map.class));
final ActionProperties actionProperties = actionPropertiesCaptor.getValue();
assertThat(actionProperties).isNotNull();
assertThat(actionProperties.getTenant()).as("event has tenant").isEqualTo("DEFAULT");

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.integration;
import static org.assertj.core.api.Assertions.assertThat;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -69,8 +70,8 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
protected static final String TENANT_EXIST = "DEFAULT";
protected static final String CREATED_BY = "CONTROLLER_PLUG_AND_PLAY";
protected ReplyToListener replyToListener;
private DeadletterListener deadletterListener;
private ReplyToListener replyToListener;
private DistributionSet distributionSet;
@Autowired
@@ -86,11 +87,12 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
Mockito.reset(deadletterListener);
replyToListener = harness.getSpy(ReplyToListener.LISTENER_ID);
assertThat(replyToListener).isNotNull();
replyToListener.purge();
Mockito.reset(replyToListener);
getDmfClient().setExchange(AmqpSettings.DMF_EXCHANGE);
}
protected <T> T waitUntilIsPresent(final Callable<Optional<T>> callable) {
private <T> T waitUntilIsPresent(final Callable<Optional<T>> callable) {
createConditionFactory().until(() -> {
return securityRule.runAsPrivileged(() -> callable.call().isPresent());
});
@@ -102,10 +104,12 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
}
}
protected void verifyDeadLetterMessages(final int expectedMessages) {
protected void waitUntilEventMessagesAreDispatchedToTarget(final EventTopic... eventTopics) {
createConditionFactory().untilAsserted(() -> {
Mockito.verify(getDeadletterListener(), Mockito.times(expectedMessages)).handleMessage(Mockito.any());
assertThat(replyToListener.getLatestEventMessageTopics())
.containsExactlyInAnyOrderElementsOf(Arrays.asList(eventTopics));
});
replyToListener.resetLatestEventMessageTopics();
}
protected DeadletterListener getDeadletterListener() {
@@ -158,7 +162,7 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
}
protected void assertRequestAttributesUpdateMessageAbsent() {
assertThat(replyToListener.getEventTopicMessages()).doesNotContainKey(EventTopic.REQUEST_ATTRIBUTES_UPDATE);
assertThat(replyToListener.getEventMessages()).doesNotContainKey(EventTopic.REQUEST_ATTRIBUTES_UPDATE);
}
protected void assertPingReplyMessage(final String correlationId) {
@@ -196,15 +200,17 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
assertThat(updatedTarget.getSecurityToken()).isEqualTo(downloadAndUpdateRequest.getTargetSecurityToken());
}
protected void assertDownloadAndInstallMessage(final Set<SoftwareModule> dsModules, final String controllerId) {
assertAssignmentMessage(dsModules, controllerId, EventTopic.DOWNLOAD_AND_INSTALL);
protected void assertDownloadAndInstallMessage(final Set<SoftwareModule> softwareModules,
final String controllerId) {
assertAssignmentMessage(softwareModules, controllerId, EventTopic.DOWNLOAD_AND_INSTALL);
}
protected void assertDownloadMessage(final Set<SoftwareModule> dsModules, final String controllerId) {
assertAssignmentMessage(dsModules, controllerId, EventTopic.DOWNLOAD);
}
protected void createAndSendTarget(final String target, final String tenant) {
protected void createAndSendThingCreated(final String target, final String tenant) {
final Message message = createTargetMessage(target, tenant);
getDmfClient().send(message);
}
@@ -229,7 +235,7 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
protected Long registerTargetAndCancelActionId(final String controllerId) {
final DistributionSetAssignmentResult assignmentResult = registerTargetAndAssignDistributionSet(controllerId);
return cancelAction(assignmentResult.getActions().get(0), controllerId);
return cancelAction(assignmentResult.getActionIds().get(0), controllerId);
}
protected void assertAllTargetsCount(final long expectedTargetsCount) {
@@ -238,7 +244,8 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
protected Message assertReplyMessageHeader(final EventTopic eventTopic, final String controllerId) {
verifyReplyToListener();
final Message replyMessage = replyToListener.getEventTopicMessages().get(eventTopic);
final Message replyMessage = replyToListener.getLatestEventMessage(eventTopic);
assertAllTargetsCount(1);
final Map<String, Object> headers = replyMessage.getMessageProperties().getHeaders();
assertThat(headers.get(MessageHeaderKey.TOPIC)).isEqualTo(eventTopic.toString());
@@ -264,7 +271,7 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
protected void registerAndAssertTargetWithExistingTenant(final String target,
final int existingTargetsAfterCreation, final TargetUpdateStatus expectedTargetStatus,
final String createdBy) {
createAndSendTarget(target, TENANT_EXIST);
createAndSendThingCreated(target, TENANT_EXIST);
final Target registerdTarget = waitUntilIsPresent(() -> targetManagement.getByControllerID(target));
assertAllTargetsCount(existingTargetsAfterCreation);
assertTarget(registerdTarget, expectedTargetStatus, createdBy);
@@ -273,7 +280,7 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
protected void registerSameTargetAndAssertBasedOnVersion(final String controllerId,
final int existingTargetsAfterCreation, final TargetUpdateStatus expectedTargetStatus) {
final int version = controllerManagement.getByControllerId(controllerId).get().getOptLockRevision();
createAndSendTarget(controllerId, TENANT_EXIST);
createAndSendThingCreated(controllerId, TENANT_EXIST);
final Target registeredTarget = waitUntilIsPresent(() -> findTargetBasedOnNewVersion(controllerId, version));
assertAllTargetsCount(existingTargetsAfterCreation);
assertThat(registeredTarget.getUpdateStatus()).isEqualTo(expectedTargetStatus);
@@ -315,7 +322,7 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
return createMessage(null, messageProperties);
}
protected Message createActionStatusUpdateMessage(final String target, final String tenant, final long actionId,
protected void createAndSendActionStatusUpdateMessage(final String target, final String tenant, final long actionId,
final DmfActionStatus status) {
final MessageProperties messageProperties = createMessagePropertiesWithTenant(tenant);
messageProperties.getHeaders().put(MessageHeaderKey.THING_ID, target);
@@ -323,7 +330,7 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
messageProperties.getHeaders().put(MessageHeaderKey.TOPIC, EventTopic.UPDATE_ACTION_STATUS.toString());
final DmfActionUpdateStatus dmfActionUpdateStatus = new DmfActionUpdateStatus(actionId, status);
return createMessage(dmfActionUpdateStatus, messageProperties);
getDmfClient().send(createMessage(dmfActionUpdateStatus, messageProperties));
}
protected MessageProperties createMessagePropertiesWithTenant(final String tenant) {

View File

@@ -8,13 +8,30 @@
*/
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.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 org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.json.model.DmfActionStatus;
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadAndUpdateRequest;
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.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;
@@ -23,6 +40,10 @@ import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.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;
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;
@@ -30,10 +51,13 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
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.SoftwareModule;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.amqp.core.Message;
@@ -42,11 +66,6 @@ import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
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")
public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpServiceIntegrationTest {
@@ -131,14 +150,222 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
final DistributionSet distributionSet2 = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
registerTargetAndAssignDistributionSet(distributionSet2.getId(), TargetUpdateStatus.PENDING,
getDistributionSet().getModules(), controllerId);
assertCancelActionMessage(assignmentResult.getActions().get(0), controllerId);
assertCancelActionMessage(assignmentResult.getActionIds().get(0), controllerId);
createAndSendTarget(controllerId, TENANT_EXIST);
createAndSendThingCreated(controllerId, TENANT_EXIST);
waitUntilTargetHasStatus(controllerId, TargetUpdateStatus.PENDING);
assertCancelActionMessage(assignmentResult.getActions().get(0), controllerId);
assertCancelActionMessage(assignmentResult.getActionIds().get(0), controllerId);
}
@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 = TargetAssignDistributionSetEvent.class, count = 0),
@Expect(type = CancelTargetAssignmentEvent.class, count = 0),
@Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 0),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) })
public void assignMultipleDsInMultiAssignMode() {
enableMultiAssignments();
final String controllerId = TARGET_PREFIX + "assignMultipleDsInMultiAssignMode";
registerAndAssertTargetWithExistingTenant(controllerId);
final Long actionId1 = assignNewDsToTarget(controllerId);
final Entry<Long, EventTopic> action1Install = new SimpleEntry<>(actionId1, EventTopic.DOWNLOAD_AND_INSTALL);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Install));
final Long actionId2 = assignNewDsToTarget(controllerId);
final Entry<Long, EventTopic> action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Install, action2Install));
}
@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 = TargetAssignDistributionSetEvent.class, count = 0),
@Expect(type = CancelTargetAssignmentEvent.class, count = 0),
@Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) })
public void cancelActionInMultiAssignMode() {
enableMultiAssignments();
final String controllerId = TARGET_PREFIX + "cancelActionInMultiAssignMode";
registerAndAssertTargetWithExistingTenant(controllerId);
final long actionId1 = assignNewDsToTarget(controllerId);
final long actionId2 = assignNewDsToTarget(controllerId);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION, EventTopic.MULTI_ACTION);
deploymentManagement.cancelAction(actionId1);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
final Entry<Long, EventTopic> action1Cancel = new SimpleEntry<>(actionId1, EventTopic.CANCEL_DOWNLOAD);
final Entry<Long, EventTopic> action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL);
assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Cancel, action2Install));
updateActionViaDmfClient(controllerId, actionId1, DmfActionStatus.CANCELED);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
assertLatestMultiActionMessage(controllerId, Arrays.asList(action2Install));
}
@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 = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 0),
@Expect(type = CancelTargetAssignmentEvent.class, count = 0),
@Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 3), @Expect(type = TargetPollEvent.class, count = 1) })
public void finishActionInMultiAssignMode() {
enableMultiAssignments();
final String controllerId = TARGET_PREFIX + "finishActionInMultiAssignMode";
registerAndAssertTargetWithExistingTenant(controllerId);
final long actionId1 = assignNewDsToTarget(controllerId);
final long actionId2 = assignNewDsToTarget(controllerId);
final Entry<Long, EventTopic> action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION, EventTopic.MULTI_ACTION);
updateActionViaDmfClient(controllerId, actionId1, DmfActionStatus.FINISHED);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.REQUEST_ATTRIBUTES_UPDATE, EventTopic.MULTI_ACTION);
assertRequestAttributesUpdateMessage(controllerId);
assertLatestMultiActionMessage(controllerId, Arrays.asList(action2Install));
}
@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 = TargetAssignDistributionSetEvent.class, count = 0),
@Expect(type = CancelTargetAssignmentEvent.class, count = 0),
@Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 0),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) })
public void assignDsMultipleTimesInMultiAssignMode() {
enableMultiAssignments();
final String controllerId = TARGET_PREFIX + "assignDsMultipleTimesInMultiAssignMode";
registerAndAssertTargetWithExistingTenant(controllerId);
final DistributionSet ds = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final Long actionId1 = assignDistributionSet(ds.getId(), controllerId).getActionIds().get(0);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
final Long actionId2 = assignDistributionSet(ds.getId(), controllerId).getActionIds().get(0);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
final Entry<Long, EventTopic> action1Install = new SimpleEntry<>(actionId1, EventTopic.DOWNLOAD_AND_INSTALL);
final Entry<Long, EventTopic> action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL);
assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Install, action2Install));
}
private void updateActionViaDmfClient(final String controllerId, final long actionId,
final DmfActionStatus status) {
createAndSendActionStatusUpdateMessage(controllerId, TENANT_EXIST, actionId, status);
}
private Long assignNewDsToTarget(final String controllerId) {
final DistributionSet ds = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final Long actionId = assignDistributionSet(ds.getId(), controllerId).getActionIds().get(0);
waitUntilTargetHasStatus(controllerId, TargetUpdateStatus.PENDING);
return actionId;
}
@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 = TargetAssignDistributionSetEvent.class, count = 0),
@Expect(type = CancelTargetAssignmentEvent.class, count = 0),
@Expect(type = ActionCreatedEvent.class, count = 2), @Expect(type = ActionUpdatedEvent.class, count = 2),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 1), @Expect(type = TargetPollEvent.class, count = 1),
@Expect(type = RolloutCreatedEvent.class, count = 2), @Expect(type = RolloutUpdatedEvent.class, count = 6),
@Expect(type = RolloutGroupCreatedEvent.class, count = 2),
@Expect(type = RolloutGroupUpdatedEvent.class, count = 4) })
public void startRolloutsWithSameDsInMultiAssignMode() {
enableMultiAssignments();
final String controllerId = TARGET_PREFIX + "startRolloutsWithSameDsInMultiAssignMode";
registerAndAssertTargetWithExistingTenant(controllerId);
final DistributionSet ds = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final Set<Long> smIds = getSoftwareModuleIds(ds);
final String filterQuery = "controllerId==" + controllerId;
createAndStartRollout(ds, filterQuery);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds));
createAndStartRollout(ds, filterQuery);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds, smIds));
}
@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 = ActionUpdatedEvent.class, count = 5),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 5), @Expect(type = TargetPollEvent.class, count = 1),
@Expect(type = TargetAttributesRequestedEvent.class, count = 2),
@Expect(type = RolloutCreatedEvent.class, count = 3), @Expect(type = RolloutUpdatedEvent.class, count = 9),
@Expect(type = RolloutGroupCreatedEvent.class, count = 3),
@Expect(type = RolloutGroupUpdatedEvent.class, count = 6) })
public void startMultipleRolloutsAndFinishInMultiAssignMode() {
enableMultiAssignments();
final String controllerId = TARGET_PREFIX + "startMultipleRolloutsAndFinishInMultiAssignMode";
registerAndAssertTargetWithExistingTenant(controllerId);
final String filterQuery = "controllerId==" + controllerId;
final DistributionSet ds1 = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final Set<Long> smIds1 = getSoftwareModuleIds(ds1);
final DistributionSet ds2 = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final Set<Long> smIds2 = getSoftwareModuleIds(ds2);
createAndStartRollout(ds1, filterQuery);
createAndStartRollout(ds2, filterQuery);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION, EventTopic.MULTI_ACTION);
createAndStartRollout(ds1, filterQuery);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION);
assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds1, smIds2, smIds1));
final List<Long> installActions = getLatestMultiActionMessageActions(controllerId).stream()
.filter(entry -> entry.getValue().equals(EventTopic.DOWNLOAD_AND_INSTALL)).map(Entry::getKey)
.collect(Collectors.toList());
updateActionViaDmfClient(controllerId, installActions.get(0), DmfActionStatus.FINISHED);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.REQUEST_ATTRIBUTES_UPDATE, EventTopic.MULTI_ACTION);
assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds2, smIds1));
updateActionViaDmfClient(controllerId, installActions.get(1), DmfActionStatus.FINISHED);
waitUntilEventMessagesAreDispatchedToTarget(EventTopic.REQUEST_ATTRIBUTES_UPDATE, EventTopic.MULTI_ACTION);
assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds1));
}
private Set<Long> getSoftwareModuleIds(final DistributionSet ds) {
return ds.getModules().stream().map(SoftwareModule::getId).collect(Collectors.toSet());
}
private Rollout createAndStartRollout(final DistributionSet ds, final String filterQuery) {
final Rollout rollout = testdataFactory.createRolloutByVariables(UUID.randomUUID().toString(), "", 1,
filterQuery, ds, "50", "5");
rolloutManagement.start(rollout.getId());
rolloutManagement.handleRollouts();
return rollout;
}
@Test
@Description("Verify that a cancel assignment send a cancel message.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@@ -154,7 +381,7 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
final Long actionId = registerTargetAndCancelActionId(controllerId);
createAndSendTarget(controllerId, TENANT_EXIST);
createAndSendThingCreated(controllerId, TENANT_EXIST);
waitUntilTargetHasStatus(controllerId, TargetUpdateStatus.PENDING);
assertCancelActionMessage(actionId, controllerId);
}
@@ -171,39 +398,28 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
assertDeleteMessage(controllerId);
}
@Test
@Description("Verify that attribute update is requested after device successfully closed software update.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 2),
@Expect(type = ActionUpdatedEvent.class, count = 2), @Expect(type = ActionCreatedEvent.class, count = 2),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 4),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),
@Expect(type = TargetPollEvent.class, count = 1) })
public void attributeRequestAfterSuccessfulUpdate() {
final String controllerId = TARGET_PREFIX + "attributeUpdateRequest";
registerAndAssertTargetWithExistingTenant(controllerId);
final Target target = controllerManagement.getByControllerId(controllerId).get();
final DistributionSet distributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final long actionId1 = assignDistributionSet(distributionSet, target).getActions().get(0);
waitUntilTargetHasStatus(controllerId, TargetUpdateStatus.PENDING);
final Message messageError = createActionStatusUpdateMessage(controllerId, TENANT_EXIST, actionId1,
DmfActionStatus.ERROR);
getDmfClient().send(messageError);
final long actionId1 = assignNewDsToTarget(controllerId);
updateActionViaDmfClient(controllerId, actionId1, DmfActionStatus.ERROR);
waitUntilTargetHasStatus(controllerId, TargetUpdateStatus.ERROR);
assertRequestAttributesUpdateMessageAbsent();
final long actionId2 = assignDistributionSet(distributionSet, target).getActions().get(0);
waitUntilTargetHasStatus(controllerId, TargetUpdateStatus.PENDING);
final Message messageFin = createActionStatusUpdateMessage(controllerId, TENANT_EXIST, actionId2,
DmfActionStatus.FINISHED);
getDmfClient().send(messageFin);
final long actionId2 = assignNewDsToTarget(controllerId);
updateActionViaDmfClient(controllerId, actionId2, DmfActionStatus.FINISHED);
waitUntilTargetHasStatus(controllerId, TargetUpdateStatus.IN_SYNC);
assertRequestAttributesUpdateMessage(controllerId);
}
@@ -220,7 +436,6 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
final String controllerId = TARGET_PREFIX + "registerTargets_1";
final DistributionSet distributionSet = createTargetAndDistributionSetAndAssign(controllerId, DOWNLOAD_ONLY);
// verify
final Message message = assertReplyMessageHeader(EventTopic.DOWNLOAD, controllerId);
Mockito.verifyZeroInteractions(getDeadletterListener());
@@ -249,4 +464,54 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
private void waitUntil(final Callable<Boolean> callable) {
createConditionFactory().until(() -> securityRule.runAsPrivileged(callable));
}
private void enableMultiAssignments() {
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, true);
}
private void assertLatestMultiActionMessageContainsInstallMessages(final String controllerId,
final List<Set<Long>> smIdsOfActionsExpected) {
final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION);
assertThat(multiactionMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID))
.isEqualTo(controllerId);
final DmfMultiActionRequest multiActionRequest = (DmfMultiActionRequest) getDmfClient().getMessageConverter()
.fromMessage(multiactionMessage);
final List<Set<Long>> smIdsOfActionsFound = getDownloadAndUpdateRequests(multiActionRequest).stream()
.map(AmqpMessageDispatcherServiceIntegrationTest::getSmIds).collect(Collectors.toList());
assertThat(smIdsOfActionsFound).containsExactlyInAnyOrderElementsOf(smIdsOfActionsExpected);
}
private void assertLatestMultiActionMessage(final String controllerId,
final List<Entry<Long, EventTopic>> actionsExpected) {
final List<Entry<Long, EventTopic>> actionsFromMessage = getLatestMultiActionMessageActions(controllerId);
assertThat(actionsFromMessage).containsExactlyInAnyOrderElementsOf(actionsExpected);
}
private List<Entry<Long, EventTopic>> getLatestMultiActionMessageActions(final String expectedControllerId) {
final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION);
assertThat(multiactionMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID))
.isEqualTo(expectedControllerId);
final List<DmfMultiActionElement> multiActionRequest = ((DmfMultiActionRequest) getDmfClient()
.getMessageConverter().fromMessage(multiactionMessage)).getElements();
return multiActionRequest.stream()
.map(request -> new SimpleEntry<>(request.getAction().getActionId(), request.getTopic()))
.collect(Collectors.toList());
}
private static Set<Long> getSmIds(final DmfDownloadAndUpdateRequest request) {
return request.getSoftwareModules().stream().map(DmfSoftwareModule::getModuleId).collect(Collectors.toSet());
}
private static List<DmfDownloadAndUpdateRequest> getDownloadAndUpdateRequests(final DmfMultiActionRequest request) {
return request.getElements().stream()
.filter(AmqpMessageDispatcherServiceIntegrationTest::isDownloadAndUpdateRequest)
.map(multiAction -> (DmfDownloadAndUpdateRequest) multiAction.getAction()).collect(Collectors.toList());
}
private static boolean isDownloadAndUpdateRequest(final DmfMultiActionElement multiActionElement) {
return multiActionElement.getTopic().equals(EventTopic.DOWNLOAD)
|| multiActionElement.getTopic().equals(EventTopic.DOWNLOAD_AND_INSTALL);
}
}

View File

@@ -104,7 +104,7 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
@Description("Tests register invalid target withy empty controller id. Tests register invalid target with null controller id")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void registerEmptyTarget() {
createAndSendTarget("", TENANT_EXIST);
createAndSendThingCreated("", TENANT_EXIST);
assertAllTargetsCount(0);
verifyOneDeadLetterMessage();
@@ -114,7 +114,7 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
@Description("Tests register invalid target with whitspace controller id. Tests register invalid target with null controller id")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void registerWhitespaceTarget() {
createAndSendTarget("Invalid Invalid", TENANT_EXIST);
createAndSendThingCreated("Invalid Invalid", TENANT_EXIST);
assertAllTargetsCount(0);
verifyOneDeadLetterMessage();
@@ -124,7 +124,7 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
@Description("Tests register invalid target with null controller id. Tests register invalid target with null controller id")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 0) })
public void registerInvalidNullTargets() {
createAndSendTarget(null, TENANT_EXIST);
createAndSendThingCreated(null, TENANT_EXIST);
assertAllTargetsCount(0);
verifyOneDeadLetterMessage();
@@ -571,13 +571,13 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
final DistributionSet distributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString());
final DistributionSetAssignmentResult distributionSetAssignmentResult = assignDistributionSet(
distributionSet.getId(), controllerId);
deploymentManagement.cancelAction(distributionSetAssignmentResult.getActions().get(0));
deploymentManagement.cancelAction(distributionSetAssignmentResult.getActionIds().get(0));
// test
registerSameTargetAndAssertBasedOnVersion(controllerId, 1, TargetUpdateStatus.PENDING);
// verify
assertCancelActionMessage(distributionSetAssignmentResult.getActions().get(0), controllerId);
assertCancelActionMessage(distributionSetAssignmentResult.getActionIds().get(0), controllerId);
Mockito.verifyZeroInteractions(getDeadletterListener());
}
@@ -908,7 +908,7 @@ public class AmqpMessageHandlerServiceIntegrationTest extends AbstractAmqpServic
private Long registerTargetAndSendActionStatus(final DmfActionStatus sendActionStatus, final String controllerId) {
final DistributionSetAssignmentResult assignmentResult = registerTargetAndAssignDistributionSet(controllerId);
final Long actionId = assignmentResult.getActions().get(0);
final Long actionId = assignmentResult.getActionIds().get(0);
sendActionUpdateStatus(new DmfActionUpdateStatus(actionId, sendActionStatus));
return actionId;
}

View File

@@ -10,8 +10,11 @@ package org.eclipse.hawkbit.integration.listener;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
@@ -26,21 +29,26 @@ public class ReplyToListener implements TestRabbitListener {
public static final String LISTENER_ID = "replyto";
public static final String REPLY_TO_QUEUE = "reply_queue";
private final Map<EventTopic, Message> eventTopicMessages = new EnumMap<>(EventTopic.class);
private final Map<EventTopic, List<Message>> eventMessages = new EnumMap<>(EventTopic.class);
private final List<EventTopic> eventMessageTopics = new ArrayList<>();
private final Map<String, Message> deleteMessages = new HashMap<>();
private final Map<String, Message> pingResponseMessages = new HashMap<>();
@Override
@RabbitListener(id = LISTENER_ID, queues = REPLY_TO_QUEUE)
public void handleMessage(final Message message) {
final MessageType messageType = MessageType
.valueOf(message.getMessageProperties().getHeaders().get(MessageHeaderKey.TYPE).toString());
if (messageType == MessageType.EVENT) {
final EventTopic eventTopic = EventTopic
.valueOf(message.getMessageProperties().getHeaders().get(MessageHeaderKey.TOPIC).toString());
eventTopicMessages.put(eventTopic, message);
eventMessageTopics.add(eventTopic);
eventMessages.merge(eventTopic, Collections.singletonList(message), (oldList, listToAdd) -> {
final List<Message> newList = new ArrayList<>(oldList);
newList.addAll(listToAdd);
return newList;
});
return;
}
@@ -62,8 +70,28 @@ public class ReplyToListener implements TestRabbitListener {
}
public Map<EventTopic, Message> getEventTopicMessages() {
return eventTopicMessages;
public void purge() {
eventMessages.clear();
deleteMessages.clear();
pingResponseMessages.clear();
eventMessageTopics.clear();
}
public List<EventTopic> getLatestEventMessageTopics() {
return eventMessageTopics;
}
public void resetLatestEventMessageTopics() {
eventMessageTopics.clear();
}
public Message getLatestEventMessage(final EventTopic eventTopic) {
final List<Message> messages = getEventMessages().get(eventTopic);
return messages == null ? null : messages.get(messages.size() - 1);
}
public Map<EventTopic, List<Message>> getEventMessages() {
return eventMessages;
}
public Map<String, Message> getDeleteMessages() {