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:
committed by
Dominic Schabel
parent
f16f986eed
commit
1ac40a7a95
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user