Dmf batch support changes. (#1273)

* Dmf batch support changes. Implement single batch message instead of multiple messages for assigment on multiple targets. Added system property to switch on/off.

Signed-off-by: Dimitar Shterev <dimitar.shterev@bosch.io>

* Dmf batch support changes. Implement single batch message instead of multiple messages for assigment on multiple targets. Added system property to switch on/off.

Signed-off-by: Dimitar Shterev <dimitar.shterev@bosch.io>

* Dmf batch support changes. Implement single batch message instead of multiple messages for assigment on multiple targets. Added system property to switch on/off.

Signed-off-by: Dimitar Shterev <dimitar.shterev@bosch.io>

* Dmf batch support changes. Implement single batch message instead of multiple messages for assigment on multiple targets. Added system property to switch on/off.

Signed-off-by: Dimitar Shterev <dimitar.shterev@bosch.io>

* Update hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java

Co-authored-by: Bondar Bogdan <36962546+bogdan-bondar@users.noreply.github.com>

* Dmf batch support changes. Implement single batch message instead of multiple messages for assigment on multiple targets. Added system property to switch on/off.

Signed-off-by: Dimitar Shterev <dimitar.shterev@bosch.io>

* Dmf batch support changes. Implement single batch message instead of multiple messages for assigment on multiple targets. Added system property to switch on/off.

Signed-off-by: Dimitar Shterev <dimitar.shterev@bosch.io>

* Dmf batch support changes. Implement code review comments.

Signed-off-by: Dimitar Shterev <dimitar.shterev@bosch.io>

Signed-off-by: Dimitar Shterev <dimitar.shterev@bosch.io>
Co-authored-by: Bondar Bogdan <36962546+bogdan-bondar@users.noreply.github.com>
This commit is contained in:
Dimitar Shterev
2022-08-30 10:55:52 +03:00
committed by GitHub
parent ccb5fa3b3f
commit bc2f228edc
13 changed files with 406 additions and 19 deletions

View File

@@ -111,7 +111,7 @@ class AmqpMessageDispatcherServiceTest extends AbstractIntegrationTest {
amqpMessageDispatcherService = new AmqpMessageDispatcherService(rabbitTemplate, senderService,
artifactUrlHandlerMock, systemSecurityContext, systemManagement, targetManagement, serviceMatcher,
distributionSetManagement, softwareModuleManagement, deploymentManagement);
distributionSetManagement, softwareModuleManagement, deploymentManagement, tenantConfigurationManagement);
}

View File

@@ -12,7 +12,9 @@ import static org.assertj.core.api.Assertions.assertThat;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -32,6 +34,7 @@ import org.eclipse.hawkbit.dmf.json.model.DmfAttributeUpdate;
import org.eclipse.hawkbit.dmf.json.model.DmfCreateThing;
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadAndUpdateRequest;
import org.eclipse.hawkbit.dmf.json.model.DmfMetadata;
import org.eclipse.hawkbit.dmf.json.model.DmfSoftwareModule;
import org.eclipse.hawkbit.integration.listener.DeadletterListener;
import org.eclipse.hawkbit.integration.listener.ReplyToListener;
import org.eclipse.hawkbit.matcher.SoftwareModuleJsonMatcher;
@@ -95,7 +98,7 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
getDmfClient().setExchange(AmqpSettings.DMF_EXCHANGE);
}
private <T> T waitUntilIsPresent(final Callable<Optional<T>> callable) {
protected <T> T waitUntilIsPresent(final Callable<Optional<T>> callable) {
createConditionFactory()
.until(() -> WithSpringAuthorityRule.runAsPrivileged(() -> callable.call().isPresent()));
@@ -194,10 +197,7 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
protected void assertDmfDownloadAndUpdateRequest(final DmfDownloadAndUpdateRequest request,
final Set<SoftwareModule> softwareModules, final String controllerId) {
assertThat(softwareModules)
.is(new HamcrestCondition<>(SoftwareModuleJsonMatcher.containsExactly(request.getSoftwareModules())));
request.getSoftwareModules().forEach(dmfModule -> assertThat(dmfModule.getMetadata()).containsExactly(
new DmfMetadata(TestdataFactory.VISIBLE_SM_MD_KEY, TestdataFactory.VISIBLE_SM_MD_VALUE)));
assertSoftwareModules(softwareModules, request.getSoftwareModules());
final Target updatedTarget = waitUntilIsPresent(() -> targetManagement.getByControllerID(controllerId));
assertThat(updatedTarget).isNotNull();
assertThat(updatedTarget.getSecurityToken()).isEqualTo(request.getTargetSecurityToken());
@@ -445,4 +445,11 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
return distributionSet;
}
protected void assertSoftwareModules(final Set<SoftwareModule> expectedSoftwareModules,
final List<DmfSoftwareModule> softwareModules) {
assertThat(expectedSoftwareModules)
.is(new HamcrestCondition<>(SoftwareModuleJsonMatcher.containsExactly(softwareModules)));
softwareModules.forEach(dmfModule -> assertThat(dmfModule.getMetadata()).containsExactly(
new DmfMetadata(TestdataFactory.VISIBLE_SM_MD_KEY, TestdataFactory.VISIBLE_SM_MD_VALUE)));
}
}

View File

@@ -9,9 +9,13 @@
package org.eclipse.hawkbit.integration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.eclipse.hawkbit.dmf.amqp.api.EventTopic.BATCH_DOWNLOAD;
import static org.eclipse.hawkbit.dmf.amqp.api.EventTopic.BATCH_DOWNLOAD_AND_INSTALL;
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 static org.eclipse.hawkbit.repository.model.Action.ActionType.FORCED;
import java.util.AbstractMap.SimpleEntry;
import java.util.Arrays;
@@ -29,10 +33,12 @@ import org.eclipse.hawkbit.dmf.amqp.api.EventTopic;
import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey;
import org.eclipse.hawkbit.dmf.json.model.DmfActionRequest;
import org.eclipse.hawkbit.dmf.json.model.DmfActionStatus;
import org.eclipse.hawkbit.dmf.json.model.DmfBatchDownloadAndUpdateRequest;
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.dmf.json.model.DmfTarget;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent;
import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent;
@@ -53,6 +59,7 @@ import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedE
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TenantConfigurationCreatedEvent;
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValueChangeNotAllowedException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.DistributionSet;
@@ -66,6 +73,8 @@ import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.mockito.Mockito;
import org.springframework.amqp.core.Message;
@@ -603,4 +612,87 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|| multiActionElement.getTopic().equals(EventTopic.DOWNLOAD_AND_INSTALL);
}
@Test
@Description("Verify payload of batch assignment download and install message.")
public void assertBatchAssignmentsDownloadAndInstall() {
assertBatchAssignmentsMessagePayload(BATCH_DOWNLOAD_AND_INSTALL);
}
@Test
@Description("Verify payload of batch assignments download only message.")
public void assertBatchAssignmentsDownloadOnly() {
assertBatchAssignmentsMessagePayload(BATCH_DOWNLOAD);
}
@Test
@Description("Verify that batch and multi-assignments can't be activated at the same time.")
void assertBatchAndMultiAssignmentsNotCompatible() {
enableBatchAssignments();
assertThatExceptionOfType(TenantConfigurationValueChangeNotAllowedException.class)
.isThrownBy(() -> enableMultiAssignments());
disableBatchAssignments();
enableMultiAssignments();
assertThatExceptionOfType(TenantConfigurationValueChangeNotAllowedException.class)
.isThrownBy(() -> enableBatchAssignments());
}
@ParameterizedTest
@EnumSource(names = { "BATCH_DOWNLOAD_AND_INSTALL", "BATCH_DOWNLOAD" })
@Description("Verify payload of batch assignments.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 3),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 3), @Expect(type = ActionUpdatedEvent.class, count = 0),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 6),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 3), @Expect(type = TargetPollEvent.class, count = 3),
@Expect(type = TenantConfigurationCreatedEvent.class, count = 1) })
void assertBatchAssignmentsMessagePayload(final EventTopic topic) {
enableBatchAssignments();
final List<String> targets = Arrays.asList("batchCtrlID1", "batchCtrlID2", "batchCtrlID3");
for (int i = 0; i < targets.size(); i++) {
registerAndAssertTargetWithExistingTenant(targets.get(i), i+1);
}
final DistributionSet ds = testdataFactory.createDistributionSet();
testdataFactory.addSoftwareModuleMetadata(ds);
assignDistributionSet(ds.getId(), targets, topic == BATCH_DOWNLOAD?DOWNLOAD_ONLY:FORCED);
waitUntilEventMessagesAreDispatchedToTarget(topic);
final Message message = replyToListener.getLatestEventMessage(topic);
final Map<String, Object> headers = message.getMessageProperties().getHeaders();
assertThat(headers).containsEntry("type", EVENT.toString());
assertThat(headers).containsEntry("topic",topic.toString());
final DmfBatchDownloadAndUpdateRequest batchRequest = (DmfBatchDownloadAndUpdateRequest) getDmfClient()
.getMessageConverter().fromMessage(message);
assertThat(batchRequest).isExactlyInstanceOf(DmfBatchDownloadAndUpdateRequest.class);
assertDmfBatchDownloadAndUpdateRequest(batchRequest, ds.getModules(), targets);
}
protected void assertDmfBatchDownloadAndUpdateRequest(final DmfBatchDownloadAndUpdateRequest request,
final Set<SoftwareModule> softwareModules,
final List<String> controllerIds) {
assertSoftwareModules(softwareModules, request.getSoftwareModules());
List<Object> tokens = controllerIds.stream().map(controllerId -> {
final Optional<Target> target = controllerManagement.getByControllerId(controllerId);
assertThat(target).isPresent();
return target.get().getSecurityToken();
}).collect(Collectors.toList());
List<DmfTarget> requestTargets = request.getTargets();
assertThat(requestTargets).hasSameSizeAs(controllerIds);
requestTargets.forEach(requestTarget -> {
assertThat(requestTarget).isNotNull();
assertThat(tokens.contains(requestTarget.getTargetSecurityToken()));
});
}
}