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:
@@ -354,10 +354,11 @@ public class AmqpConfiguration {
|
||||
final AmqpMessageSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler,
|
||||
final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement,
|
||||
final TargetManagement targetManagement, final DistributionSetManagement distributionSetManagement,
|
||||
final SoftwareModuleManagement softwareModuleManagement, final DeploymentManagement deploymentManagement) {
|
||||
final SoftwareModuleManagement softwareModuleManagement, final DeploymentManagement deploymentManagement,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement) {
|
||||
return new AmqpMessageDispatcherService(rabbitTemplate, amqpSenderService, artifactUrlHandler,
|
||||
systemSecurityContext, systemManagement, targetManagement, serviceMatcher, distributionSetManagement,
|
||||
softwareModuleManagement, deploymentManagement);
|
||||
softwareModuleManagement, deploymentManagement, tenantConfigurationManagement);
|
||||
}
|
||||
|
||||
private static Map<String, Object> getTTLMaxArgsAuthenticationQueue() {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package org.eclipse.hawkbit.amqp;
|
||||
|
||||
import static org.eclipse.hawkbit.repository.RepositoryConstants.MAX_ACTION_COUNT;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
@@ -32,16 +33,19 @@ import org.eclipse.hawkbit.dmf.amqp.api.MessageType;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfActionRequest;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfArtifact;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfArtifactHash;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfBatchDownloadAndUpdateRequest;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadAndUpdateRequest;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfMetadata;
|
||||
import org.eclipse.hawkbit.dmf.json.model.DmfMultiActionRequest;
|
||||
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.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.event.remote.MultiActionEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
|
||||
@@ -89,6 +93,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
private final DistributionSetManagement distributionSetManagement;
|
||||
private final DeploymentManagement deploymentManagement;
|
||||
private final SoftwareModuleManagement softwareModuleManagement;
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -110,13 +115,17 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
* cluster node
|
||||
* @param distributionSetManagement
|
||||
* to retrieve modules
|
||||
* @param tenantConfigurationManagement
|
||||
* to access tenant configuration
|
||||
*
|
||||
*/
|
||||
protected AmqpMessageDispatcherService(final RabbitTemplate rabbitTemplate,
|
||||
final AmqpMessageSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler,
|
||||
final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement,
|
||||
final TargetManagement targetManagement, final ServiceMatcher serviceMatcher,
|
||||
final DistributionSetManagement distributionSetManagement,
|
||||
final SoftwareModuleManagement softwareModuleManagement, final DeploymentManagement deploymentManagement) {
|
||||
final AmqpMessageSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler,
|
||||
final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement,
|
||||
final TargetManagement targetManagement, final ServiceMatcher serviceMatcher,
|
||||
final DistributionSetManagement distributionSetManagement,
|
||||
final SoftwareModuleManagement softwareModuleManagement, final DeploymentManagement deploymentManagement,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement) {
|
||||
super(rabbitTemplate);
|
||||
this.artifactUrlHandler = artifactUrlHandler;
|
||||
this.amqpSenderService = amqpSenderService;
|
||||
@@ -127,6 +136,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.softwareModuleManagement = softwareModuleManagement;
|
||||
this.deploymentManagement = deploymentManagement;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,8 +192,13 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
distributionSetManagement.get(assignedEvent.getDistributionSetId()).ifPresent(ds -> {
|
||||
final Map<SoftwareModule, List<SoftwareModuleMetadata>> softwareModules = getSoftwareModulesWithMetadata(
|
||||
ds);
|
||||
targets.forEach(target -> sendUpdateMessageToTarget(
|
||||
assignedEvent.getActions().get(target.getControllerId()), target, softwareModules));
|
||||
|
||||
if (!targets.isEmpty() && isBatchAssignmentsEnabled()) {
|
||||
sendUpdateMessageToTargets(assignedEvent.getActions(), targets, softwareModules);
|
||||
} else {
|
||||
targets.forEach(target -> sendUpdateMessageToTarget(
|
||||
assignedEvent.getActions().get(target.getControllerId()), target, softwareModules));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -500,4 +515,59 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
||||
PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT), module.getId()).getContent();
|
||||
}
|
||||
|
||||
private void sendUpdateMessageToTargets(final Map<String, ActionProperties> actions, final List<Target> targets,
|
||||
final Map<SoftwareModule, List<SoftwareModuleMetadata>> modules) {
|
||||
|
||||
List<DmfTarget> dmfTargets = targets.stream().filter(target -> IpUtil.isAmqpUri(target.getAddress()))
|
||||
.map(t -> convertToDmfTarget(t, actions.get(t.getControllerId()).getId())).collect(Collectors.toList());
|
||||
|
||||
final DmfBatchDownloadAndUpdateRequest batchRequest = new DmfBatchDownloadAndUpdateRequest();
|
||||
batchRequest.setTimestamp(System.currentTimeMillis());
|
||||
batchRequest.addTargets(dmfTargets);
|
||||
|
||||
//due to the fact that all targets in a batch use the same set of software modules we don't generate
|
||||
// target-specific urls
|
||||
Target firstTarget = targets.get(0);
|
||||
if (modules != null) {
|
||||
modules.entrySet().forEach(entry ->
|
||||
batchRequest.addSoftwareModule(convertToAmqpSoftwareModule(firstTarget, entry)));
|
||||
}
|
||||
|
||||
// we use only the first action when constructing message as Tenant and action type are the same
|
||||
// since all actions have the same trigger
|
||||
final ActionProperties firstAction = actions.values().iterator().next();
|
||||
final Message message = getMessageConverter().toMessage(batchRequest,
|
||||
createMessagePropertiesBatch(firstAction.getTenant(), getBatchEventTopicForAction(firstAction)));
|
||||
amqpSenderService.sendMessage(message, firstTarget.getAddress());
|
||||
}
|
||||
|
||||
protected DmfTarget convertToDmfTarget(final Target target, final Long actionId) {
|
||||
DmfTarget dmfTarget = new DmfTarget();
|
||||
dmfTarget.setActionId(actionId);
|
||||
dmfTarget.setControllerId(target.getControllerId());
|
||||
dmfTarget.setTargetSecurityToken(systemSecurityContext.runAsSystem(target::getSecurityToken));
|
||||
return dmfTarget;
|
||||
}
|
||||
|
||||
private static MessageProperties createMessagePropertiesBatch(final String tenant, final EventTopic topic) {
|
||||
final MessageProperties messageProperties = new MessageProperties();
|
||||
messageProperties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
|
||||
messageProperties.setHeader(MessageHeaderKey.CONTENT_TYPE, MessageProperties.CONTENT_TYPE_JSON);
|
||||
messageProperties.setHeader(MessageHeaderKey.TENANT, tenant);
|
||||
|
||||
messageProperties.setHeader(MessageHeaderKey.TOPIC, topic);
|
||||
messageProperties.setHeader(MessageHeaderKey.TYPE, MessageType.EVENT);
|
||||
return messageProperties;
|
||||
}
|
||||
|
||||
public boolean isBatchAssignmentsEnabled() {
|
||||
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
|
||||
.getConfigurationValue(BATCH_ASSIGNMENTS_ENABLED, Boolean.class).getValue());
|
||||
}
|
||||
|
||||
private static EventTopic getBatchEventTopicForAction(final ActionProperties action) {
|
||||
return (Action.ActionType.DOWNLOAD_ONLY == action.getActionType() || !action.isMaintenanceWindowAvailable())
|
||||
? EventTopic.BATCH_DOWNLOAD
|
||||
: EventTopic.BATCH_DOWNLOAD_AND_INSTALL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,16 @@ public enum EventTopic {
|
||||
/**
|
||||
* Topic to send multiple actions to the device.
|
||||
*/
|
||||
MULTI_ACTION
|
||||
MULTI_ACTION,
|
||||
|
||||
/**
|
||||
* Topic when sending a download only action to multiple devices.
|
||||
*/
|
||||
BATCH_DOWNLOAD,
|
||||
|
||||
/**
|
||||
* Topic when sending a download and install action to multiple devices.
|
||||
*/
|
||||
BATCH_DOWNLOAD_AND_INSTALL
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Bosch.IO GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.dmf.json.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* JSON representation of batch download and update request.
|
||||
*
|
||||
*/
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class DmfBatchDownloadAndUpdateRequest {
|
||||
|
||||
@JsonProperty
|
||||
private Long timestamp;
|
||||
|
||||
@JsonProperty
|
||||
private List<DmfTarget> targets;
|
||||
|
||||
@JsonProperty
|
||||
private List<DmfSoftwareModule> softwareModules;
|
||||
|
||||
public Long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(final Long timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public List<DmfSoftwareModule> getSoftwareModules() {
|
||||
if (softwareModules == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(softwareModules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Software module.
|
||||
*
|
||||
* @param createSoftwareModule
|
||||
* the module
|
||||
*/
|
||||
public void addSoftwareModule(final DmfSoftwareModule createSoftwareModule) {
|
||||
if (softwareModules == null) {
|
||||
softwareModules = new ArrayList<>();
|
||||
}
|
||||
|
||||
softwareModules.add(createSoftwareModule);
|
||||
}
|
||||
|
||||
public List<DmfTarget> getTargets() {
|
||||
if (targets == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(targets);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a Target.
|
||||
*
|
||||
* @param target
|
||||
* the target
|
||||
*/
|
||||
public void addTarget(final DmfTarget target) {
|
||||
if (targets == null) {
|
||||
targets = new ArrayList<>();
|
||||
}
|
||||
|
||||
targets.add(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add multiple Targets.
|
||||
*
|
||||
* @param targets
|
||||
* the target
|
||||
*/
|
||||
public void addTargets(final List<DmfTarget> targets) {
|
||||
if (this.targets == null) {
|
||||
this.targets = new ArrayList<>();
|
||||
}
|
||||
this.targets.addAll(targets);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Copyright (c) 2022 Bosch.IO GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.dmf.json.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
|
||||
/**
|
||||
* Json representation of Target used in batch download and update request.
|
||||
*
|
||||
*/
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class DmfTarget {
|
||||
|
||||
@JsonProperty
|
||||
private Long actionId;
|
||||
|
||||
@JsonProperty
|
||||
private String controllerId;
|
||||
|
||||
@JsonProperty
|
||||
private String targetSecurityToken;
|
||||
|
||||
public Long getActionId() {
|
||||
return actionId;
|
||||
}
|
||||
|
||||
public void setActionId(final Long actionId) {
|
||||
this.actionId = actionId;
|
||||
}
|
||||
|
||||
public String getControllerId() {
|
||||
return controllerId;
|
||||
}
|
||||
|
||||
public void setControllerId(final String controllerId) {
|
||||
this.controllerId = controllerId;
|
||||
}
|
||||
|
||||
public String getTargetSecurityToken() {
|
||||
return targetSecurityToken;
|
||||
}
|
||||
|
||||
public void setTargetSecurityToken(final String targetSecurityToken) {
|
||||
this.targetSecurityToken = targetSecurityToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"DmfTarget [actionId=%d controllerId='%s']",
|
||||
actionId, controllerId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user