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 AmqpMessageSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler,
|
||||||
final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement,
|
final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement,
|
||||||
final TargetManagement targetManagement, final DistributionSetManagement distributionSetManagement,
|
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,
|
return new AmqpMessageDispatcherService(rabbitTemplate, amqpSenderService, artifactUrlHandler,
|
||||||
systemSecurityContext, systemManagement, targetManagement, serviceMatcher, distributionSetManagement,
|
systemSecurityContext, systemManagement, targetManagement, serviceMatcher, distributionSetManagement,
|
||||||
softwareModuleManagement, deploymentManagement);
|
softwareModuleManagement, deploymentManagement, tenantConfigurationManagement);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, Object> getTTLMaxArgsAuthenticationQueue() {
|
private static Map<String, Object> getTTLMaxArgsAuthenticationQueue() {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
package org.eclipse.hawkbit.amqp;
|
package org.eclipse.hawkbit.amqp;
|
||||||
|
|
||||||
import static org.eclipse.hawkbit.repository.RepositoryConstants.MAX_ACTION_COUNT;
|
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.net.URI;
|
||||||
import java.util.Collections;
|
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.DmfActionRequest;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfArtifact;
|
import org.eclipse.hawkbit.dmf.json.model.DmfArtifact;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfArtifactHash;
|
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.DmfDownloadAndUpdateRequest;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfMetadata;
|
import org.eclipse.hawkbit.dmf.json.model.DmfMetadata;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfMultiActionRequest;
|
import org.eclipse.hawkbit.dmf.json.model.DmfMultiActionRequest;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfSoftwareModule;
|
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.DeploymentManagement;
|
||||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||||
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
||||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
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.MultiActionEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent;
|
||||||
@@ -89,6 +93,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
|||||||
private final DistributionSetManagement distributionSetManagement;
|
private final DistributionSetManagement distributionSetManagement;
|
||||||
private final DeploymentManagement deploymentManagement;
|
private final DeploymentManagement deploymentManagement;
|
||||||
private final SoftwareModuleManagement softwareModuleManagement;
|
private final SoftwareModuleManagement softwareModuleManagement;
|
||||||
|
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@@ -110,13 +115,17 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
|||||||
* cluster node
|
* cluster node
|
||||||
* @param distributionSetManagement
|
* @param distributionSetManagement
|
||||||
* to retrieve modules
|
* to retrieve modules
|
||||||
|
* @param tenantConfigurationManagement
|
||||||
|
* to access tenant configuration
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
protected AmqpMessageDispatcherService(final RabbitTemplate rabbitTemplate,
|
protected AmqpMessageDispatcherService(final RabbitTemplate rabbitTemplate,
|
||||||
final AmqpMessageSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler,
|
final AmqpMessageSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler,
|
||||||
final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement,
|
final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement,
|
||||||
final TargetManagement targetManagement, final ServiceMatcher serviceMatcher,
|
final TargetManagement targetManagement, final ServiceMatcher serviceMatcher,
|
||||||
final DistributionSetManagement distributionSetManagement,
|
final DistributionSetManagement distributionSetManagement,
|
||||||
final SoftwareModuleManagement softwareModuleManagement, final DeploymentManagement deploymentManagement) {
|
final SoftwareModuleManagement softwareModuleManagement, final DeploymentManagement deploymentManagement,
|
||||||
|
final TenantConfigurationManagement tenantConfigurationManagement) {
|
||||||
super(rabbitTemplate);
|
super(rabbitTemplate);
|
||||||
this.artifactUrlHandler = artifactUrlHandler;
|
this.artifactUrlHandler = artifactUrlHandler;
|
||||||
this.amqpSenderService = amqpSenderService;
|
this.amqpSenderService = amqpSenderService;
|
||||||
@@ -127,6 +136,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
|||||||
this.distributionSetManagement = distributionSetManagement;
|
this.distributionSetManagement = distributionSetManagement;
|
||||||
this.softwareModuleManagement = softwareModuleManagement;
|
this.softwareModuleManagement = softwareModuleManagement;
|
||||||
this.deploymentManagement = deploymentManagement;
|
this.deploymentManagement = deploymentManagement;
|
||||||
|
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -182,8 +192,13 @@ public class AmqpMessageDispatcherService extends BaseAmqpService {
|
|||||||
distributionSetManagement.get(assignedEvent.getDistributionSetId()).ifPresent(ds -> {
|
distributionSetManagement.get(assignedEvent.getDistributionSetId()).ifPresent(ds -> {
|
||||||
final Map<SoftwareModule, List<SoftwareModuleMetadata>> softwareModules = getSoftwareModulesWithMetadata(
|
final Map<SoftwareModule, List<SoftwareModuleMetadata>> softwareModules = getSoftwareModulesWithMetadata(
|
||||||
ds);
|
ds);
|
||||||
|
|
||||||
|
if (!targets.isEmpty() && isBatchAssignmentsEnabled()) {
|
||||||
|
sendUpdateMessageToTargets(assignedEvent.getActions(), targets, softwareModules);
|
||||||
|
} else {
|
||||||
targets.forEach(target -> sendUpdateMessageToTarget(
|
targets.forEach(target -> sendUpdateMessageToTarget(
|
||||||
assignedEvent.getActions().get(target.getControllerId()), target, softwareModules));
|
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();
|
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,
|
amqpMessageDispatcherService = new AmqpMessageDispatcherService(rabbitTemplate, senderService,
|
||||||
artifactUrlHandlerMock, systemSecurityContext, systemManagement, targetManagement, serviceMatcher,
|
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.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
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.DmfCreateThing;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadAndUpdateRequest;
|
import org.eclipse.hawkbit.dmf.json.model.DmfDownloadAndUpdateRequest;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfMetadata;
|
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.DeadletterListener;
|
||||||
import org.eclipse.hawkbit.integration.listener.ReplyToListener;
|
import org.eclipse.hawkbit.integration.listener.ReplyToListener;
|
||||||
import org.eclipse.hawkbit.matcher.SoftwareModuleJsonMatcher;
|
import org.eclipse.hawkbit.matcher.SoftwareModuleJsonMatcher;
|
||||||
@@ -95,7 +98,7 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
|
|||||||
getDmfClient().setExchange(AmqpSettings.DMF_EXCHANGE);
|
getDmfClient().setExchange(AmqpSettings.DMF_EXCHANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T waitUntilIsPresent(final Callable<Optional<T>> callable) {
|
protected <T> T waitUntilIsPresent(final Callable<Optional<T>> callable) {
|
||||||
|
|
||||||
createConditionFactory()
|
createConditionFactory()
|
||||||
.until(() -> WithSpringAuthorityRule.runAsPrivileged(() -> callable.call().isPresent()));
|
.until(() -> WithSpringAuthorityRule.runAsPrivileged(() -> callable.call().isPresent()));
|
||||||
@@ -194,10 +197,7 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
|
|||||||
|
|
||||||
protected void assertDmfDownloadAndUpdateRequest(final DmfDownloadAndUpdateRequest request,
|
protected void assertDmfDownloadAndUpdateRequest(final DmfDownloadAndUpdateRequest request,
|
||||||
final Set<SoftwareModule> softwareModules, final String controllerId) {
|
final Set<SoftwareModule> softwareModules, final String controllerId) {
|
||||||
assertThat(softwareModules)
|
assertSoftwareModules(softwareModules, request.getSoftwareModules());
|
||||||
.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)));
|
|
||||||
final Target updatedTarget = waitUntilIsPresent(() -> targetManagement.getByControllerID(controllerId));
|
final Target updatedTarget = waitUntilIsPresent(() -> targetManagement.getByControllerID(controllerId));
|
||||||
assertThat(updatedTarget).isNotNull();
|
assertThat(updatedTarget).isNotNull();
|
||||||
assertThat(updatedTarget.getSecurityToken()).isEqualTo(request.getTargetSecurityToken());
|
assertThat(updatedTarget.getSecurityToken()).isEqualTo(request.getTargetSecurityToken());
|
||||||
@@ -445,4 +445,11 @@ public abstract class AbstractAmqpServiceIntegrationTest extends AbstractAmqpInt
|
|||||||
return distributionSet;
|
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;
|
package org.eclipse.hawkbit.integration;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
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.EventTopic.DOWNLOAD;
|
||||||
import static org.eclipse.hawkbit.dmf.amqp.api.MessageType.EVENT;
|
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.DOWNLOAD_ONLY;
|
||||||
|
import static org.eclipse.hawkbit.repository.model.Action.ActionType.FORCED;
|
||||||
|
|
||||||
import java.util.AbstractMap.SimpleEntry;
|
import java.util.AbstractMap.SimpleEntry;
|
||||||
import java.util.Arrays;
|
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.amqp.api.MessageHeaderKey;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfActionRequest;
|
import org.eclipse.hawkbit.dmf.json.model.DmfActionRequest;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfActionStatus;
|
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.DmfDownloadAndUpdateRequest;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfMultiActionRequest;
|
import org.eclipse.hawkbit.dmf.json.model.DmfMultiActionRequest;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfMultiActionRequest.DmfMultiActionElement;
|
import org.eclipse.hawkbit.dmf.json.model.DmfMultiActionRequest.DmfMultiActionElement;
|
||||||
import org.eclipse.hawkbit.dmf.json.model.DmfSoftwareModule;
|
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.DeploymentManagement;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent;
|
import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent;
|
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.TargetCreatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||||
import org.eclipse.hawkbit.repository.event.remote.entity.TenantConfigurationCreatedEvent;
|
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.jpa.model.JpaTarget;
|
||||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
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.matcher.ExpectEvents;
|
||||||
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
|
import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
|
||||||
import org.junit.jupiter.api.Test;
|
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.mockito.Mockito;
|
||||||
import org.springframework.amqp.core.Message;
|
import org.springframework.amqp.core.Message;
|
||||||
|
|
||||||
@@ -603,4 +612,87 @@ public class AmqpMessageDispatcherServiceIntegrationTest extends AbstractAmqpSer
|
|||||||
|| multiActionElement.getTopic().equals(EventTopic.DOWNLOAD_AND_INSTALL);
|
|| 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.
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -147,6 +147,11 @@ public class TenantConfigurationProperties {
|
|||||||
*/
|
*/
|
||||||
public static final String MULTI_ASSIGNMENTS_ENABLED = "multi.assignments.enabled";
|
public static final String MULTI_ASSIGNMENTS_ENABLED = "multi.assignments.enabled";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch to enable/disable the batch-assignment feature.
|
||||||
|
*/
|
||||||
|
public static final String BATCH_ASSIGNMENTS_ENABLED = "batch.assignments.enabled";
|
||||||
|
|
||||||
private String keyName;
|
private String keyName;
|
||||||
private String defaultValue = "";
|
private String defaultValue = "";
|
||||||
private Class<?> dataType = String.class;
|
private Class<?> dataType = String.class;
|
||||||
|
|||||||
@@ -96,5 +96,10 @@ hawkbit.server.tenant.configuration.multi-assignments-enabled.defaultValue=false
|
|||||||
hawkbit.server.tenant.configuration.multi-assignments-enabled.dataType=java.lang.Boolean
|
hawkbit.server.tenant.configuration.multi-assignments-enabled.dataType=java.lang.Boolean
|
||||||
hawkbit.server.tenant.configuration.multi-assignments-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
|
hawkbit.server.tenant.configuration.multi-assignments-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
|
||||||
|
|
||||||
|
hawkbit.server.tenant.configuration.batch-assignments-enabled.keyName=batch.assignments.enabled
|
||||||
|
hawkbit.server.tenant.configuration.batch-assignments-enabled.defaultValue=false
|
||||||
|
hawkbit.server.tenant.configuration.batch-assignments-enabled.dataType=java.lang.Boolean
|
||||||
|
hawkbit.server.tenant.configuration.batch-assignments-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
|
||||||
|
|
||||||
|
|
||||||
# Default tenant configuration - END
|
# Default tenant configuration - END
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.repository.jpa;
|
package org.eclipse.hawkbit.repository.jpa;
|
||||||
|
|
||||||
|
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED;
|
||||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
|
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
|
||||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED;
|
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED;
|
||||||
|
|
||||||
@@ -177,9 +178,9 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
|||||||
* Asserts that the requested configuration value change is allowed. Throws
|
* Asserts that the requested configuration value change is allowed. Throws
|
||||||
* a {@link TenantConfigurationValueChangeNotAllowedException} otherwise.
|
* a {@link TenantConfigurationValueChangeNotAllowedException} otherwise.
|
||||||
*
|
*
|
||||||
* @param configurationKeyName
|
* @param key
|
||||||
* The configuration key.
|
* The configuration key.
|
||||||
* @param tenantConfiguration
|
* @param valueChange
|
||||||
* The configuration to be validated.
|
* The configuration to be validated.
|
||||||
*
|
*
|
||||||
* @throws TenantConfigurationValueChangeNotAllowedException
|
* @throws TenantConfigurationValueChangeNotAllowedException
|
||||||
@@ -188,6 +189,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
|||||||
private void assertValueChangeIsAllowed(final String key, final JpaTenantConfiguration valueChange) {
|
private void assertValueChangeIsAllowed(final String key, final JpaTenantConfiguration valueChange) {
|
||||||
assertMultiAssignmentsValueChange(key, valueChange);
|
assertMultiAssignmentsValueChange(key, valueChange);
|
||||||
assertAutoCloseValueChange(key, valueChange);
|
assertAutoCloseValueChange(key, valueChange);
|
||||||
|
assertBatchAssignmentValueChange(key, valueChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("squid:S1172")
|
@SuppressWarnings("squid:S1172")
|
||||||
@@ -201,11 +203,30 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertMultiAssignmentsValueChange(final String key, final JpaTenantConfiguration valueChange) {
|
private void assertMultiAssignmentsValueChange(final String key, final JpaTenantConfiguration valueChange) {
|
||||||
if (MULTI_ASSIGNMENTS_ENABLED.equals(key) && !Boolean.parseBoolean(valueChange.getValue())) {
|
if (MULTI_ASSIGNMENTS_ENABLED.equals(key) && !Boolean.parseBoolean(valueChange.getValue())) {
|
||||||
LOG.debug("The Multi-Assignments '{}' feature cannot be disabled.", key);
|
LOG.debug("The Multi-Assignments '{}' feature cannot be disabled.", key);
|
||||||
throw new TenantConfigurationValueChangeNotAllowedException();
|
throw new TenantConfigurationValueChangeNotAllowedException();
|
||||||
}
|
}
|
||||||
|
if (MULTI_ASSIGNMENTS_ENABLED.equals(key) && Boolean.parseBoolean(valueChange.getValue())) {
|
||||||
|
JpaTenantConfiguration batchConfig = tenantConfigurationRepository.findByKey(BATCH_ASSIGNMENTS_ENABLED);
|
||||||
|
if (batchConfig!=null && Boolean.parseBoolean(batchConfig.getValue())) {
|
||||||
|
LOG.debug("The Multi-Assignments '{}' feature cannot be enabled as it contradicts with " +
|
||||||
|
"The Batch-Assignments feature, which is already enabled .", key);
|
||||||
|
throw new TenantConfigurationValueChangeNotAllowedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertBatchAssignmentValueChange(final String key, final JpaTenantConfiguration valueChange) {
|
||||||
|
if (BATCH_ASSIGNMENTS_ENABLED.equals(key) && Boolean.parseBoolean(valueChange.getValue())) {
|
||||||
|
JpaTenantConfiguration multiConfig = tenantConfigurationRepository.findByKey(MULTI_ASSIGNMENTS_ENABLED);
|
||||||
|
if (multiConfig != null && Boolean.parseBoolean(multiConfig.getValue())) {
|
||||||
|
LOG.debug("The Batch-Assignments '{}' feature cannot be enabled as it contradicts with " +
|
||||||
|
"The Multi-Assignments feature, which is already enabled .", key);
|
||||||
|
throw new TenantConfigurationValueChangeNotAllowedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -465,4 +465,12 @@ public abstract class AbstractIntegrationTest {
|
|||||||
protected static Comparator<Target> controllerIdComparator() {
|
protected static Comparator<Target> controllerIdComparator() {
|
||||||
return (o1, o2) -> o1.getControllerId().equals(o2.getControllerId()) ? 0 : 1;
|
return (o1, o2) -> o1.getControllerId().equals(o2.getControllerId()) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void enableBatchAssignments() {
|
||||||
|
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void disableBatchAssignments() {
|
||||||
|
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ public class TenantResourceDocumentationTest extends AbstractApiRestDocumentatio
|
|||||||
"the expiry time in milliseconds that needs to elapse before an action may be cleaned up.");
|
"the expiry time in milliseconds that needs to elapse before an action may be cleaned up.");
|
||||||
CONFIG_ITEM_DESCRIPTIONS.put(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED,
|
CONFIG_ITEM_DESCRIPTIONS.put(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED,
|
||||||
"if multiple distribution sets can be assigned to the same targets.");
|
"if multiple distribution sets can be assigned to the same targets.");
|
||||||
|
CONFIG_ITEM_DESCRIPTIONS.put(TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED,
|
||||||
|
"if distribution set can be assigned to multiple targets in a single batch message.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|||||||
Reference in New Issue
Block a user