From e797215a53291b68aa40ec266eaa053351a9b541 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Wed, 6 Mar 2019 16:43:55 +0100 Subject: [PATCH 01/56] First draft of new assignment logic Signed-off-by: Stefan Behl --- .../TenantConfigurationProperties.java | 5 +++ .../hawkbit-repository-defaults.properties | 6 +++ .../jpa/JpaDeploymentManagement.java | 41 +++++++++++++++---- 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationProperties.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationProperties.java index dab824d7ba..628b67f914 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationProperties.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/tenancy/configuration/TenantConfigurationProperties.java @@ -142,6 +142,11 @@ public static class TenantConfigurationKey { */ public static final String ACTION_CLEANUP_ACTION_STATUS = "action.cleanup.actionStatus"; + /** + * Switch to enable/disable the multi-assignment feature. + */ + public static final String MULTI_ASSIGNMENTS_ENABLED = "multi.assignments.enabled"; + private String keyName; private String defaultValue = ""; private Class dataType = String.class; diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/resources/hawkbit-repository-defaults.properties b/hawkbit-repository/hawkbit-repository-core/src/main/resources/hawkbit-repository-defaults.properties index e363bbd850..8b8d99808c 100644 --- a/hawkbit-repository/hawkbit-repository-core/src/main/resources/hawkbit-repository-defaults.properties +++ b/hawkbit-repository/hawkbit-repository-core/src/main/resources/hawkbit-repository-defaults.properties @@ -91,4 +91,10 @@ hawkbit.server.tenant.configuration.action-cleanup-action-expiry.validator=org.e hawkbit.server.tenant.configuration.action-cleanup-action-status.keyName=action.cleanup.actionStatus hawkbit.server.tenant.configuration.action-cleanup-action-status.defaultValue=CANCELED,ERROR +hawkbit.server.tenant.configuration.multi-assignments-enabled.keyName=multi.assignments.enabled +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.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator + + # Default tenant configuration - END diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index 3a154b338d..0b91551d8f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -8,6 +8,10 @@ */ package org.eclipse.hawkbit.repository.jpa; +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 java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -63,6 +67,7 @@ import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetWithActionType; +import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.tenancy.TenantAware; @@ -342,6 +347,12 @@ private void assertMaxTargetsPerManualAssignmentQuota(final Long distributionSet private Set closeOrCancelActiveActions(final AbstractDsAssignmentStrategy assignmentStrategy, final List> targetIdsChunks) { + + if (isMultiAssignmentsEnabled()) { + LOG.info(">>> multi-assignments are enabled: No need to close /cancel active actions."); + return Collections.emptySet(); + } + if (isActionsAutocloseEnabled()) { assignmentStrategy.closeActiveActions(targetIdsChunks); return Collections.emptySet(); @@ -350,18 +361,16 @@ private Set closeOrCancelActiveActions(final AbstractDsAssignmentStrategy } } - protected boolean isActionsAutocloseEnabled() { - return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement - .getConfigurationValue(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, Boolean.class) - .getValue()); - } - @Override @Transactional(isolation = Isolation.READ_COMMITTED) @Retryable(include = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) public void cancelInactiveScheduledActionsForTargets(final List targetIds) { - actionRepository.switchStatus(Status.CANCELED, targetIds, false, Status.SCHEDULED); + if (!isMultiAssignmentsEnabled()) { + actionRepository.switchStatus(Status.CANCELED, targetIds, false, Status.SCHEDULED); + } else { + LOG.info(">>> multi-assignments are enabled: No need to cancel inactive scheduled actions."); + } } private void setAssignedDistributionSetAndTargetUpdateStatus(final AbstractDsAssignmentStrategy assignmentStrategy, @@ -782,4 +791,22 @@ private static String formatInClause(final Collection elements) { protected ActionRepository getActionRepository() { return actionRepository; } + + protected boolean isActionsAutocloseEnabled() { + return getConfigValue(REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, Boolean.class, Boolean.FALSE); + } + + private boolean isMultiAssignmentsEnabled() { + return getConfigValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class, Boolean.TRUE); + } + + private T getConfigValue(final String key, final Class valueType, + final T defaultValue) { + return systemSecurityContext.runAsSystem(() -> { + final TenantConfigurationValue configEntry = tenantConfigurationManagement.getConfigurationValue(key, + valueType); + return configEntry != null ? configEntry.getValue() : defaultValue; + }); + } + } From 63e95f69b89e3b8eaedf66f88d1aa1db65a52a97 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Thu, 7 Mar 2019 16:46:55 +0100 Subject: [PATCH 02/56] Enhancements of System Configuration view Signed-off-by: Stefan Behl --- .../TenantResourceDocumentationTest.java | 2 + .../RepositoryConfigurationView.java | 38 ++++++- .../MultiAssignmentsConfigurationItem.java | 103 ++++++++++++++++++ .../ui/utils/UIComponentIdProvider.java | 6 + .../src/main/resources/messages.properties | 2 + 5 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/repository/MultiAssignmentsConfigurationItem.java diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TenantResourceDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TenantResourceDocumentationTest.java index 2194ddd4c7..603d096609 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TenantResourceDocumentationTest.java +++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TenantResourceDocumentationTest.java @@ -88,6 +88,8 @@ public class TenantResourceDocumentationTest extends AbstractApiRestDocumentatio "the list of action status that should be taken into account for the cleanup."); CONFIG_ITEM_DESCRIPTIONS.put(TenantConfigurationKey.ACTION_CLEANUP_ACTION_EXPIRY, "the expiry time in milliseconds that needs to elapse before an action may be cleaned up."); + CONFIG_ITEM_DESCRIPTIONS.put(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, + "if multiple distribution sets can be assigned to the same targets."); } @Autowired diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RepositoryConfigurationView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RepositoryConfigurationView.java index a41dc53e80..fb3d3bf81a 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RepositoryConfigurationView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RepositoryConfigurationView.java @@ -13,6 +13,7 @@ import org.eclipse.hawkbit.ui.tenantconfiguration.generic.BooleanConfigurationItem; import org.eclipse.hawkbit.ui.tenantconfiguration.repository.ActionAutocleanupConfigurationItem; import org.eclipse.hawkbit.ui.tenantconfiguration.repository.ActionAutocloseConfigurationItem; +import org.eclipse.hawkbit.ui.tenantconfiguration.repository.MultiAssignmentsConfigurationItem; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; @@ -40,10 +41,14 @@ public class RepositoryConfigurationView extends BaseConfigurationView private final ActionAutocleanupConfigurationItem actionAutocleanupConfigurationItem; + private final MultiAssignmentsConfigurationItem multiAssignmentsConfigurationItem; + private CheckBox actionAutocloseCheckBox; private CheckBox actionAutocleanupCheckBox; + private CheckBox multiAssignmentsCheckBox; + RepositoryConfigurationView(final VaadinMessageSource i18n, final TenantConfigurationManagement tenantConfigurationManagement) { this.i18n = i18n; @@ -51,6 +56,8 @@ public class RepositoryConfigurationView extends BaseConfigurationView i18n); this.actionAutocleanupConfigurationItem = new ActionAutocleanupConfigurationItem(tenantConfigurationManagement, i18n); + this.multiAssignmentsConfigurationItem = new MultiAssignmentsConfigurationItem(tenantConfigurationManagement, + i18n); init(); } @@ -70,27 +77,39 @@ private void init() { header.addStyleName("config-panel-header"); vLayout.addComponent(header); - final GridLayout gridLayout = new GridLayout(2, 2); + final GridLayout gridLayout = new GridLayout(2, 3); gridLayout.setSpacing(true); gridLayout.setImmediate(true); gridLayout.setColumnExpandRatio(1, 1.0F); gridLayout.setSizeFull(); + final boolean isMultiAssignmentsEnabled = multiAssignmentsConfigurationItem.isConfigEnabled(); + actionAutocloseCheckBox = SPUIComponentProvider.getCheckBox("", DIST_CHECKBOX_STYLE, null, false, ""); actionAutocloseCheckBox.setId(UIComponentIdProvider.REPOSITORY_ACTIONS_AUTOCLOSE_CHECKBOX); + actionAutocloseCheckBox.setEnabled(!isMultiAssignmentsEnabled); + actionAutocloseConfigurationItem.setEnabled(!isMultiAssignmentsEnabled); actionAutocloseCheckBox.setValue(actionAutocloseConfigurationItem.isConfigEnabled()); actionAutocloseCheckBox.addValueChangeListener(this); actionAutocloseConfigurationItem.addChangeListener(this); gridLayout.addComponent(actionAutocloseCheckBox, 0, 0); gridLayout.addComponent(actionAutocloseConfigurationItem, 1, 0); + multiAssignmentsCheckBox = SPUIComponentProvider.getCheckBox("", DIST_CHECKBOX_STYLE, null, false, ""); + multiAssignmentsCheckBox.setId(UIComponentIdProvider.REPOSITORY_MULTI_ASSIGNMENTS_CHECKBOX); + multiAssignmentsCheckBox.setValue(multiAssignmentsConfigurationItem.isConfigEnabled()); + multiAssignmentsCheckBox.addValueChangeListener(this); + multiAssignmentsConfigurationItem.addChangeListener(this); + gridLayout.addComponent(multiAssignmentsCheckBox, 0, 1); + gridLayout.addComponent(multiAssignmentsConfigurationItem, 1, 1); + actionAutocleanupCheckBox = SPUIComponentProvider.getCheckBox("", DIST_CHECKBOX_STYLE, null, false, ""); actionAutocleanupCheckBox.setId(UIComponentIdProvider.REPOSITORY_ACTIONS_AUTOCLEANUP_CHECKBOX); actionAutocleanupCheckBox.setValue(actionAutocleanupConfigurationItem.isConfigEnabled()); actionAutocleanupCheckBox.addValueChangeListener(this); actionAutocleanupConfigurationItem.addChangeListener(this); - gridLayout.addComponent(actionAutocleanupCheckBox, 0, 1); - gridLayout.addComponent(actionAutocleanupConfigurationItem, 1, 1); + gridLayout.addComponent(actionAutocleanupCheckBox, 0, 2); + gridLayout.addComponent(actionAutocleanupConfigurationItem, 1, 2); vLayout.addComponent(gridLayout); rootPanel.setContent(vLayout); @@ -101,18 +120,25 @@ private void init() { public void save() { actionAutocloseConfigurationItem.save(); actionAutocleanupConfigurationItem.save(); + multiAssignmentsConfigurationItem.save(); } @Override public boolean isUserInputValid() { return actionAutocloseConfigurationItem.isUserInputValid() - && actionAutocleanupConfigurationItem.isUserInputValid(); + && actionAutocleanupConfigurationItem.isUserInputValid() + && multiAssignmentsConfigurationItem.isUserInputValid(); } @Override public void undo() { + multiAssignmentsConfigurationItem.undo(); + final boolean isMultiAssignmentsEnabled = multiAssignmentsConfigurationItem.isConfigEnabled(); + multiAssignmentsCheckBox.setValue(isMultiAssignmentsEnabled); actionAutocloseConfigurationItem.undo(); actionAutocloseCheckBox.setValue(actionAutocloseConfigurationItem.isConfigEnabled()); + actionAutocloseCheckBox.setEnabled(!isMultiAssignmentsEnabled); + actionAutocloseConfigurationItem.setEnabled(!isMultiAssignmentsEnabled); actionAutocleanupConfigurationItem.undo(); actionAutocleanupCheckBox.setValue(actionAutocleanupConfigurationItem.isConfigEnabled()); } @@ -138,6 +164,10 @@ public void valueChange(final ValueChangeEvent event) { configurationItem = actionAutocloseConfigurationItem; } else if (actionAutocleanupCheckBox.equals(checkBox)) { configurationItem = actionAutocleanupConfigurationItem; + } else if (multiAssignmentsCheckBox.equals(checkBox)) { + configurationItem = multiAssignmentsConfigurationItem; + actionAutocloseCheckBox.setEnabled(!checkBox.getValue()); + actionAutocloseConfigurationItem.setEnabled(!checkBox.getValue()); } else { return; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/repository/MultiAssignmentsConfigurationItem.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/repository/MultiAssignmentsConfigurationItem.java new file mode 100644 index 0000000000..8fee392d2e --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/repository/MultiAssignmentsConfigurationItem.java @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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.ui.tenantconfiguration.repository; + +import org.eclipse.hawkbit.repository.TenantConfigurationManagement; +import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; +import org.eclipse.hawkbit.ui.common.builder.LabelBuilder; +import org.eclipse.hawkbit.ui.tenantconfiguration.generic.AbstractBooleanTenantConfigurationItem; +import org.eclipse.hawkbit.ui.utils.VaadinMessageSource; + +import com.vaadin.ui.Label; +import com.vaadin.ui.VerticalLayout; + +/** + * This class represents the UI item for the target security token section in + * the authentication configuration view. + */ +public class MultiAssignmentsConfigurationItem extends AbstractBooleanTenantConfigurationItem { + + private static final long serialVersionUID = 1L; + + private static final String MSG_KEY_CHECKBOX = "label.configuration.repository.multiassignments"; + private static final String MSG_KEY_NOTICE = "label.configuration.repository.multiassignments.notice"; + + private final VerticalLayout container; + private final VaadinMessageSource i18n; + + private boolean isMultiAssignmentsEnabled; + private boolean multiAssignmentsEnabledChanged; + + public MultiAssignmentsConfigurationItem(final TenantConfigurationManagement tenantConfigurationManagement, + final VaadinMessageSource i18n) { + super(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, tenantConfigurationManagement, i18n); + this.i18n = i18n; + + super.init(MSG_KEY_CHECKBOX); + isMultiAssignmentsEnabled = isConfigEnabled(); + + container = new VerticalLayout(); + container.setImmediate(true); + + container.addComponent(newLabel(MSG_KEY_NOTICE)); + + if (isMultiAssignmentsEnabled) { + setSettingsVisible(isMultiAssignmentsEnabled); + } + + } + + @Override + public void configEnable() { + if (!isMultiAssignmentsEnabled) { + multiAssignmentsEnabledChanged = true; + } + isMultiAssignmentsEnabled = true; + setSettingsVisible(true); + } + + @Override + public void configDisable() { + if (isMultiAssignmentsEnabled) { + multiAssignmentsEnabledChanged = true; + } + isMultiAssignmentsEnabled = false; + setSettingsVisible(false); + } + + @Override + public void save() { + if (!multiAssignmentsEnabledChanged) { + return; + } + getTenantConfigurationManagement().addOrUpdateConfiguration(getConfigurationKey(), isMultiAssignmentsEnabled); + } + + @Override + public void undo() { + multiAssignmentsEnabledChanged = false; + isMultiAssignmentsEnabled = getTenantConfigurationManagement() + .getConfigurationValue(getConfigurationKey(), Boolean.class).getValue(); + } + + private void setSettingsVisible(final boolean visible) { + if (visible) { + addComponent(container); + } else { + removeComponent(container); + } + } + + private Label newLabel(final String msgKey) { + final Label label = new LabelBuilder().name(i18n.getMessage(msgKey)).buildLabel(); + label.setWidthUndefined(); + return label; + } + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java index 7cb73df052..3273702b37 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java @@ -1231,6 +1231,12 @@ public final class UIComponentIdProvider { */ public static final String REPOSITORY_ACTIONS_AUTOCLEANUP_CHECKBOX = "repositoryactionsautocleanupcheckbox"; + /** + * Configuration checkbox for + * {@link TenantConfigurationKey#MULTI_ASSIGNMENTS_ENABLED}. + */ + public static final String REPOSITORY_MULTI_ASSIGNMENTS_CHECKBOX = "repositorymultiassignmentscheckbox"; + /** * Configuration checkbox for * {@link TenantConfigurationKey#ROLLOUT_APPROVAL_ENABLED} diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index 940bf1e1a8..d6415f1dd6 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -239,6 +239,8 @@ label.configuration.repository.autocleanup.action.suffix = day(s) label.configuration.repository.autocleanup.action.expiry.invalid = The specified number of days is invalid. Please enter a positive integer value between 1 and 1000. label.configuration.anonymous.download = Allow targets to download artifacts without security credentials label.configuration.repository.autocleanup.action.notice = Warning: The actions are deleted from the repository and cannot be restored +label.configuration.repository.multiassignments = Allow parallel execution of multiple distribution set assignments and rollouts +label.configuration.repository.multiassignments.notice = Warning: Once this assignment behavior is active, it cannot be deactivated. label.unsupported.browser.ie = Sorry! Your current browser is not supported. Please use Internet Explorer 11 and above label.auto.assign.description = When an auto assign distribution set is selected, it will be automatically assigned to all targets that match the target filter. label.auto.assign.enable = Enable auto assignment From aa8144816440ea6c0acb6565e7791572e3bd7e57 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Fri, 8 Mar 2019 15:17:21 +0100 Subject: [PATCH 03/56] Drag&drop enhancements for multiple distribution set assignments Signed-off-by: Stefan Behl --- .../hawkbit/ui/management/DeploymentView.java | 6 +- .../management/dstable/DistributionTable.java | 77 +++++----- .../dstable/DistributionTableLayout.java | 4 +- .../management/state/AssignmentUIState.java | 80 +++++++++++ .../management/state/ManagementUIState.java | 8 +- .../management/targettable/TargetTable.java | 131 ++++++++++-------- .../src/main/resources/messages.properties | 1 + 7 files changed, 203 insertions(+), 104 deletions(-) create mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/AssignmentUIState.java diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/DeploymentView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/DeploymentView.java index 97f97df534..7ac5641616 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/DeploymentView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/DeploymentView.java @@ -22,6 +22,7 @@ import org.eclipse.hawkbit.repository.TargetFilterQueryManagement; import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TargetTagManagement; +import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.ui.AbstractHawkbitUI; import org.eclipse.hawkbit.ui.SpPermissionChecker; import org.eclipse.hawkbit.ui.UiProperties; @@ -127,6 +128,7 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW final TargetTagManagement targetTagManagement, final DistributionSetTagManagement distributionSetTagManagement, final TargetFilterQueryManagement targetFilterQueryManagement, final SystemManagement systemManagement, + final TenantConfigurationManagement configManagement, final NotificationUnreadButton notificationUnreadButton, final DeploymentViewMenuItem deploymentViewMenuItem, @Qualifier("uiExecutor") final Executor uiExecutor) { super(eventBus, notificationUnreadButton); @@ -147,7 +149,7 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW targetFilterQueryManagement, targetTagManagement); final TargetTable targetTable = new TargetTable(eventBus, i18n, uiNotification, targetManagement, managementUIState, permChecker, managementViewClientCriterion, distributionSetManagement, - targetTagManagement, deploymentManagement, uiProperties); + targetTagManagement, deploymentManagement, configManagement, uiProperties); this.countMessageLabel = new CountMessageLabel(eventBus, targetManagement, i18n, managementUIState, targetTable); @@ -175,7 +177,7 @@ public class DeploymentView extends AbstractNotificationView implements BrowserW this.distributionTableLayout = new DistributionTableLayout(i18n, eventBus, permChecker, managementUIState, distributionSetManagement, distributionSetTypeManagement, managementViewClientCriterion, entityFactory, uiNotification, distributionSetTagManagement, targetTagManagement, systemManagement, - targetManagement, deploymentManagement, uiProperties); + targetManagement, deploymentManagement, configManagement, uiProperties); } else { this.distributionTagLayout = null; this.distributionTableLayout = null; diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java index 2ce756834f..ec2013b443 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java @@ -26,6 +26,7 @@ import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper; import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TargetTagManagement; +import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent; import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException; import org.eclipse.hawkbit.repository.model.Action.ActionType; @@ -35,6 +36,7 @@ import org.eclipse.hawkbit.repository.model.RepositoryModelConstants; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetWithActionType; +import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties; import org.eclipse.hawkbit.ui.SpPermissionChecker; import org.eclipse.hawkbit.ui.UiProperties; import org.eclipse.hawkbit.ui.common.ConfirmationDialog; @@ -53,6 +55,7 @@ import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupLayout; import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupLayout.ActionTypeOption; import org.eclipse.hawkbit.ui.management.miscs.MaintenanceWindowLayout; +import org.eclipse.hawkbit.ui.management.state.AssignmentUIState; import org.eclipse.hawkbit.ui.management.state.ManagementUIState; import org.eclipse.hawkbit.ui.management.targettable.TargetTable; import org.eclipse.hawkbit.ui.push.DistributionSetUpdatedEventContainer; @@ -117,6 +120,8 @@ public class DistributionTable extends AbstractNamedVersionTable targetDetailsL private void addNewDistributionToAssignmentList(final List targetDetailsList, final DistributionSet distributionSet) { - String pendingActionMessage = null; final DistributionSetIdName distributionSetIdName = new DistributionSetIdName(distributionSet); - + String pendingActionMessage = null; + final AssignmentUIState assignmentState = managementUIState.getAssignmentState(); for (final Target target : targetDetailsList) { - final TargetIdName key = new TargetIdName(target); - if (managementUIState.getAssignedList().keySet().contains(key) - && managementUIState.getAssignedList().get(key).equals(distributionSetIdName)) { + final TargetIdName targetIdName = new TargetIdName(target); + if (assignmentState.isAssignedTo(distributionSetIdName, targetIdName) && !isMultiAssignmentsEnabled()) { pendingActionMessage = getPendingActionMessage(pendingActionMessage, target.getControllerId(), HawkbitCommonUtil.getDistributionNameAndVersion(distributionSetIdName.getName(), distributionSetIdName.getVersion())); getNotification().displayValidationError(pendingActionMessage); } else { - managementUIState.getAssignedList().put(key, distributionSetIdName); + assignmentState.addAssignment(distributionSetIdName, targetIdName); } } } @@ -473,7 +479,7 @@ confirmQuestion, getI18n().getMessage(UIMessageIdProvider.BUTTON_OK), if (ok && isMaintenanceWindowValid()) { saveAllAssignments(); } else { - managementUIState.getAssignedList().clear(); + managementUIState.getAssignmentState().clear(); } }, createAssignmentTab(), UIComponentIdProvider.DIST_SET_TO_TARGET_ASSIGNMENT_CONFIRM_ID); } @@ -505,10 +511,7 @@ private boolean isMaintenanceWindowValid() { } private void saveAllAssignments() { - final Set itemIds = managementUIState.getAssignedList().keySet(); - Long distId; - List targetIdSetList; - List tempIdList; + final ActionType actionType = ((ActionTypeOptionGroupLayout.ActionTypeOption) actionTypeOptionGroupLayout .getActionTypeOptionGroup().getValue()).getActionType(); final long forcedTimeStamp = (((ActionTypeOptionGroupLayout.ActionTypeOption) actionTypeOptionGroupLayout @@ -516,30 +519,16 @@ private void saveAllAssignments() { ? actionTypeOptionGroupLayout.getForcedTimeDateField().getValue().getTime() : RepositoryModelConstants.NO_FORCE_TIME; - final Map> saveAssignedList = Maps.newHashMapWithExpectedSize(itemIds.size()); - - for (final TargetIdName itemId : itemIds) { - final DistributionSetIdName distitem = managementUIState.getAssignedList().get(itemId); - distId = distitem.getId(); - - if (saveAssignedList.containsKey(distId)) { - targetIdSetList = saveAssignedList.get(distId); - } else { - targetIdSetList = new ArrayList<>(); - } - targetIdSetList.add(itemId); - saveAssignedList.put(distId, targetIdSetList); - } - final String maintenanceSchedule = maintenanceWindowLayout.getMaintenanceSchedule(); final String maintenanceDuration = maintenanceWindowLayout.getMaintenanceDuration(); final String maintenanceTimeZone = maintenanceWindowLayout.getMaintenanceTimeZone(); - for (final Map.Entry> mapEntry : saveAssignedList.entrySet()) { - tempIdList = saveAssignedList.get(mapEntry.getKey()); + final AssignmentUIState assignmentState = managementUIState.getAssignmentState(); + assignmentState.forEach(distributionSetIdName -> { + final Set targetIds = assignmentState.getAssignedTargets(distributionSetIdName); final DistributionSetAssignmentResult distributionSetAssignmentResult = deploymentManagement - .assignDistributionSet(mapEntry.getKey(), - tempIdList.stream().map(t -> maintenanceWindowLayout.isEnabled() + .assignDistributionSet(distributionSetIdName.getId(), + targetIds.stream().map(t -> maintenanceWindowLayout.isEnabled() ? new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp, maintenanceSchedule, maintenanceDuration, maintenanceTimeZone) : new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp)) @@ -553,27 +542,26 @@ private void saveAllAssignments() { getNotification().displaySuccess(getI18n().getMessage("message.target.alreadyAssigned", distributionSetAssignmentResult.getAlreadyAssigned())); } - } - resfreshPinnedDetails(saveAssignedList); + }); - managementUIState.getAssignedList().clear(); + refreshPinnedDetails(assignmentState); + + assignmentState.clear(); getNotification().displaySuccess(getI18n().getMessage("message.target.ds.assign.success")); getEventBus().publish(this, SaveActionWindowEvent.SAVED_ASSIGNMENTS); } - private void resfreshPinnedDetails(final Map> saveAssignedList) { + private void refreshPinnedDetails(final AssignmentUIState assignmentState) { final Optional pinnedDist = managementUIState.getTargetTableFilters().getPinnedDistId(); final Optional pinnedTarget = managementUIState.getDistributionTableFilters().getPinnedTarget(); if (pinnedDist.isPresent()) { - if (saveAssignedList.keySet().contains(pinnedDist.get())) { + final Long pinnedDistSet = pinnedDist.get(); + if (assignmentState.isAssigned(pinnedDistSet)) { getEventBus().publish(this, PinUnpinEvent.PIN_DISTRIBUTION); } - } else if (pinnedTarget.isPresent()) { - final Set assignedTargetIds = managementUIState.getAssignedList().keySet(); - if (assignedTargetIds.contains(pinnedTarget.get())) { - getEventBus().publish(this, PinUnpinEvent.PIN_TARGET); - } + } else if (pinnedTarget.isPresent() && assignmentState.hasAssignments(pinnedTarget.get())) { + getEventBus().publish(this, PinUnpinEvent.PIN_TARGET); } } @@ -912,4 +900,11 @@ protected String getDeletedEntityName(final Long entityId) { return ""; } + private boolean isMultiAssignmentsEnabled() { + return configManagement + .getConfigurationValue(TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, + Boolean.class) + .getValue(); + } + } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTableLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTableLayout.java index c7a4711796..b7e403228e 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTableLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTableLayout.java @@ -16,6 +16,7 @@ import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TargetTagManagement; +import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.ui.SpPermissionChecker; import org.eclipse.hawkbit.ui.UiProperties; import org.eclipse.hawkbit.ui.common.table.AbstractTableLayout; @@ -43,6 +44,7 @@ public DistributionTableLayout(final VaadinMessageSource i18n, final UIEventBus final UINotification notification, final DistributionSetTagManagement distributionSetTagManagement, final TargetTagManagement targetTagManagement, final SystemManagement systemManagement, final TargetManagement targetManagement, final DeploymentManagement deploymentManagement, + final TenantConfigurationManagement configManagement, final UiProperties uiProperties) { final DistributionAddUpdateWindowLayout distributionAddUpdateWindowLayout = new DistributionAddUpdateWindowLayout( @@ -54,7 +56,7 @@ public DistributionTableLayout(final VaadinMessageSource i18n, final UIEventBus this.distributionTable = new DistributionTable(eventBus, i18n, permissionChecker, notification, managementUIState, managementViewClientCriterion, targetManagement, distributionSetManagement, - deploymentManagement, targetTagManagement, uiProperties); + deploymentManagement, targetTagManagement, configManagement, uiProperties); super.init(i18n, new DistributionTableHeader(i18n, permissionChecker, eventBus, managementUIState), distributionTable, diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/AssignmentUIState.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/AssignmentUIState.java new file mode 100644 index 0000000000..903f04a960 --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/AssignmentUIState.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2019 Bosch Software Innovations 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.ui.management.state; + +import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.hawkbit.ui.common.entity.DistributionSetIdName; +import org.eclipse.hawkbit.ui.common.entity.TargetIdName; + +/** + * State keeping track of the distribution set assignments that were done in the + * UI. + */ +public class AssignmentUIState implements Serializable, Iterable { + + private static final long serialVersionUID = 1L; + + private final Map> assignments = new HashMap<>(); + + private final Set assignedTargets = new HashSet<>(); + + public Set getAssignedTargets(final DistributionSetIdName distributionSet) { + if (!assignments.containsKey(distributionSet)) { + return Collections.emptySet(); + } + return assignments.get(distributionSet); + } + + public void addAssignment(final DistributionSetIdName distributionSet, final Set targets) { + assignedTargets.addAll(targets); + assignments.computeIfAbsent(distributionSet, key -> new HashSet<>()).addAll(targets); + } + + public void addAssignment(final DistributionSetIdName distributionSet, final TargetIdName target) { + assignedTargets.add(target); + assignments.computeIfAbsent(distributionSet, key -> new HashSet<>()).add(target); + } + + public boolean hasAssignments(final TargetIdName target) { + return assignedTargets.contains(target); + } + + public boolean isAssignedTo(final DistributionSetIdName distributionSet, final TargetIdName target) { + if (!assignments.containsKey(distributionSet)) { + return false; + } + return assignments.get(distributionSet).contains(target); + } + + public boolean isAssigned(final DistributionSetIdName distributionSet) { + return assignments.containsKey(distributionSet); + } + + public boolean isAssigned(final Long distributionSetId) { + return assignments.keySet().stream().anyMatch(key -> distributionSetId.equals(key.getId())); + } + + public void clear() { + assignments.clear(); + assignedTargets.clear(); + } + + @Override + public Iterator iterator() { + return assignments.keySet().iterator(); + } + +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java index 90c4893765..c959a5d92f 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java @@ -10,9 +10,7 @@ import java.io.Serializable; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicLong; @@ -40,7 +38,7 @@ public class ManagementUIState implements ManagementEntityState, Serializable { private final TargetTableFilters targetTableFilters; - private final Map assignedList = new HashMap<>(); + private final AssignmentUIState assignmentState = new AssignmentUIState(); private final Set deletedDistributionList = new HashSet<>(); @@ -137,8 +135,8 @@ public DistributionTableFilters getDistributionTableFilters() { return distributionTableFilters; } - public Map getAssignedList() { - return assignedList; + public AssignmentUIState getAssignmentState() { + return assignmentState; } public Set getDeletedDistributionList() { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java index 49e17d00f1..5feb4034ae 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java @@ -29,6 +29,7 @@ import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper; import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TargetTagManagement; +import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.event.remote.entity.RemoteEntityEvent; import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException; import org.eclipse.hawkbit.repository.model.Action.ActionType; @@ -41,6 +42,7 @@ import org.eclipse.hawkbit.repository.model.TargetTagAssignmentResult; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetWithActionType; +import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties; import org.eclipse.hawkbit.ui.SpPermissionChecker; import org.eclipse.hawkbit.ui.UiProperties; import org.eclipse.hawkbit.ui.common.ConfirmationDialog; @@ -63,6 +65,7 @@ import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupLayout; import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupLayout.ActionTypeOption; import org.eclipse.hawkbit.ui.management.miscs.MaintenanceWindowLayout; +import org.eclipse.hawkbit.ui.management.state.AssignmentUIState; import org.eclipse.hawkbit.ui.management.state.ManagementUIState; import org.eclipse.hawkbit.ui.management.state.TargetTableFilters; import org.eclipse.hawkbit.ui.push.CancelTargetAssignmentEventContainer; @@ -122,6 +125,8 @@ public class TargetTable extends AbstractTable { private static final int PROPERTY_DEPT = 3; + private static final String MESSAGE_ASSIGN_TARGET_TO_MULTIPLE_DISTRIBUTIONS = "message.confirm.assign.multiple.entities.multiple.distributions"; + private final transient TargetManagement targetManagement; private final transient DistributionSetManagement distributionSetManagement; @@ -130,6 +135,8 @@ public class TargetTable extends AbstractTable { private final transient DeploymentManagement deploymentManagement; + private final transient TenantConfigurationManagement configManagement; + private final ManagementViewClientCriterion managementViewClientCriterion; private final ManagementUIState managementUIState; @@ -150,7 +157,8 @@ public TargetTable(final UIEventBus eventBus, final VaadinMessageSource i18n, fi final TargetManagement targetManagement, final ManagementUIState managementUIState, final SpPermissionChecker permChecker, final ManagementViewClientCriterion managementViewClientCriterion, final DistributionSetManagement distributionSetManagement, final TargetTagManagement tagManagement, - final DeploymentManagement deploymentManagement, final UiProperties uiProperties) { + final DeploymentManagement deploymentManagement, final TenantConfigurationManagement configManagement, + final UiProperties uiProperties) { super(eventBus, i18n, notification, permChecker); this.targetManagement = targetManagement; this.managementViewClientCriterion = managementViewClientCriterion; @@ -158,6 +166,7 @@ public TargetTable(final UIEventBus eventBus, final VaadinMessageSource i18n, fi this.distributionSetManagement = distributionSetManagement; this.tagManagement = tagManagement; this.deploymentManagement = deploymentManagement; + this.configManagement = configManagement; this.uiProperties = uiProperties; this.actionTypeOptionGroupLayout = new ActionTypeOptionGroupLayout(i18n); this.maintenanceWindowLayout = new MaintenanceWindowLayout(i18n); @@ -860,10 +869,7 @@ private boolean isFilteredByTags() { // Code for assignment start private void saveAllAssignments() { - final Set itemIds = managementUIState.getAssignedList().keySet(); - Long distId; - List targetIdSetList; - List tempIdList; + final ActionType actionType = ((ActionTypeOptionGroupLayout.ActionTypeOption) actionTypeOptionGroupLayout .getActionTypeOptionGroup().getValue()).getActionType(); final long forcedTimeStamp = (((ActionTypeOptionGroupLayout.ActionTypeOption) actionTypeOptionGroupLayout @@ -871,30 +877,16 @@ private void saveAllAssignments() { ? actionTypeOptionGroupLayout.getForcedTimeDateField().getValue().getTime() : RepositoryModelConstants.NO_FORCE_TIME; - final Map> saveAssignedList = Maps.newHashMapWithExpectedSize(itemIds.size()); - - for (final TargetIdName itemId : itemIds) { - final DistributionSetIdName distitem = managementUIState.getAssignedList().get(itemId); - distId = distitem.getId(); - - if (saveAssignedList.containsKey(distId)) { - targetIdSetList = saveAssignedList.get(distId); - } else { - targetIdSetList = new ArrayList<>(); - } - targetIdSetList.add(itemId); - saveAssignedList.put(distId, targetIdSetList); - } - final String maintenanceSchedule = maintenanceWindowLayout.getMaintenanceSchedule(); final String maintenanceDuration = maintenanceWindowLayout.getMaintenanceDuration(); final String maintenanceTimeZone = maintenanceWindowLayout.getMaintenanceTimeZone(); - for (final Map.Entry> mapEntry : saveAssignedList.entrySet()) { - tempIdList = saveAssignedList.get(mapEntry.getKey()); + final AssignmentUIState assignmentState = managementUIState.getAssignmentState(); + assignmentState.forEach(distributionSetIdName -> { + final Set targetIds = assignmentState.getAssignedTargets(distributionSetIdName); final DistributionSetAssignmentResult distributionSetAssignmentResult = deploymentManagement - .assignDistributionSet(mapEntry.getKey(), - tempIdList.stream().map(t -> maintenanceWindowLayout.isEnabled() + .assignDistributionSet(distributionSetIdName.getId(), + targetIds.stream().map(t -> maintenanceWindowLayout.isEnabled() ? new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp, maintenanceSchedule, maintenanceDuration, maintenanceTimeZone) : new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp)) @@ -908,27 +900,25 @@ private void saveAllAssignments() { getNotification().displaySuccess(getI18n().getMessage("message.target.alreadyAssigned", distributionSetAssignmentResult.getAlreadyAssigned())); } - } - resfreshPinnedDetails(saveAssignedList); + }); + refreshPinnedDetails(assignmentState); - managementUIState.getAssignedList().clear(); + assignmentState.clear(); getNotification().displaySuccess(getI18n().getMessage("message.target.ds.assign.success")); getEventBus().publish(this, SaveActionWindowEvent.SAVED_ASSIGNMENTS); } - private void resfreshPinnedDetails(final Map> saveAssignedList) { + private void refreshPinnedDetails(final AssignmentUIState assignmentState) { final Optional pinnedDist = managementUIState.getTargetTableFilters().getPinnedDistId(); final Optional pinnedTarget = managementUIState.getDistributionTableFilters().getPinnedTarget(); if (pinnedDist.isPresent()) { - if (saveAssignedList.keySet().contains(pinnedDist.get())) { + final Long pinnedDistSet = pinnedDist.get(); + if (assignmentState.isAssigned(pinnedDistSet)) { getEventBus().publish(this, PinUnpinEvent.PIN_DISTRIBUTION); } - } else if (pinnedTarget.isPresent()) { - final Set assignedTargetIds = managementUIState.getAssignedList().keySet(); - if (assignedTargetIds.contains(pinnedTarget.get())) { - getEventBus().publish(this, PinUnpinEvent.PIN_TARGET); - } + } else if (pinnedTarget.isPresent() && assignmentState.hasAssignments(pinnedTarget.get())) { + getEventBus().publish(this, PinUnpinEvent.PIN_TARGET); } } @@ -950,13 +940,17 @@ private boolean isMaintenanceWindowValid() { private void assignDsToTarget(final DragAndDropEvent event) { final TableTransferable transferable = (TableTransferable) event.getTransferable(); final AbstractTable source = (AbstractTable) transferable.getSourceComponent(); - final Set ids = source.getSelectedEntitiesByTransferable(transferable); - // only one distribution can be assigned to a target - final Long idToSelect = ids.iterator().next(); - selectDraggedEntities(source, new HashSet<>(Arrays.asList(idToSelect))); + + final Set dsIds = filterDistributionSetsToAssign(source.getSelectedEntitiesByTransferable(transferable)); + if (dsIds.isEmpty()) { + getNotification().displayWarning(getI18n().getMessage(DISTRIBUTIONSET_NOT_EXISTS)); + return; + } + + selectDraggedEntities(source, dsIds); final AbstractSelectTargetDetails dropData = (AbstractSelectTargetDetails) event.getTargetDetails(); final Object targetItemId = dropData.getItemIdOver(); - LOG.debug("Adding a log to check if targetItemId is null : {} ", targetItemId); + LOG.debug("Drop target: {} ", targetItemId); if (targetItemId == null) { getNotification().displayWarning(getI18n().getMessage(TARGETS_NOT_EXISTS, "")); return; @@ -970,28 +964,48 @@ private void assignDsToTarget(final DragAndDropEvent event) { return; } - final TargetIdName createTargetIdName = new TargetIdName(target.get()); - final List findDistributionSetById = distributionSetManagement - .get(new HashSet<>(Arrays.asList(idToSelect))); - - if (findDistributionSetById.isEmpty()) { + final List distributionSets = distributionSetManagement.get(dsIds); + if (distributionSets.isEmpty()) { getNotification().displayWarning(getI18n().getMessage(DISTRIBUTIONSET_NOT_EXISTS)); return; } - final DistributionSet distributionSetToAssign = findDistributionSetById.get(0); - addNewTargetToAssignmentList(createTargetIdName, distributionSetToAssign); - openConfirmationWindowForAssignment(distributionSetToAssign.getName(), createTargetIdName.getTargetName()); + openConfirmationWindowForAssignments(target.get(), distributionSets); + } + + private Set filterDistributionSetsToAssign(final Set ids) { + if (isMultiAssignmentsEnabled()) { + return new HashSet<>(ids); + } + if (!ids.isEmpty()) { + return Collections.singleton(ids.iterator().next()); + } + return Collections.emptySet(); + } + + private void openConfirmationWindowForAssignments(final Target target, + final List distributionSets) { + final TargetIdName targetIdName = new TargetIdName(target); + addAssignments(targetIdName, distributionSets); + openConfirmationWindowForAssignment(targetIdName.getTargetName(), + distributionSets.stream().map(DistributionSet::getName).collect(Collectors.toList())); } - private void openConfirmationWindowForAssignment(final String distributionNameToAssign, final String targetName) { + private void openConfirmationWindowForAssignment(final String targetName, final List distributionSetNames) { + final String confirmationMessage; + if (distributionSetNames.size() > 1) { + confirmationMessage = getI18n().getMessage(MESSAGE_ASSIGN_TARGET_TO_MULTIPLE_DISTRIBUTIONS, "target", + targetName, String.join(", ", distributionSetNames)); + } else { + confirmationMessage = getI18n().getMessage(MESSAGE_CONFIRM_ASSIGN_ENTITY, distributionSetNames.get(0), + "target", targetName); + } confirmDialog = new ConfirmationDialog(getI18n().getMessage(CAPTION_ENTITY_ASSIGN_ACTION_CONFIRMBOX), - getI18n().getMessage(MESSAGE_CONFIRM_ASSIGN_ENTITY, distributionNameToAssign, "target", targetName), - getI18n().getMessage(UIMessageIdProvider.BUTTON_OK), + confirmationMessage, getI18n().getMessage(UIMessageIdProvider.BUTTON_OK), getI18n().getMessage(UIMessageIdProvider.BUTTON_CANCEL), ok -> { if (ok && isMaintenanceWindowValid()) { saveAllAssignments(); } else { - managementUIState.getAssignedList().clear(); + managementUIState.getAssignmentState().clear(); } }, createAssignmentTab(), UIComponentIdProvider.DIST_SET_TO_TARGET_ASSIGNMENT_CONFIRM_ID); UI.getCurrent().addWindow(confirmDialog.getWindow()); @@ -1049,10 +1063,10 @@ private void enableSaveButton(final boolean enabled) { confirmDialog.getOkButton().setEnabled(enabled); } - private void addNewTargetToAssignmentList(final TargetIdName createTargetIdName, - final DistributionSet findDistributionSetAllById) { - final DistributionSetIdName distributionNameId = new DistributionSetIdName(findDistributionSetAllById); - managementUIState.getAssignedList().put(createTargetIdName, distributionNameId); + private void addAssignments(final TargetIdName targetIdName, final List distributionSets) { + final AssignmentUIState assignmentState = managementUIState.getAssignmentState(); + distributionSets.stream().map(DistributionSetIdName::new) + .forEach(distributionSet -> assignmentState.addAssignment(distributionSet, targetIdName)); } @Override @@ -1104,4 +1118,11 @@ protected String getDeletedEntityName(final Long entityId) { return ""; } + private boolean isMultiAssignmentsEnabled() { + return configManagement + .getConfigurationValue(TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, + Boolean.class) + .getValue(); + } + } diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index d6415f1dd6..c0c76844c7 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -686,6 +686,7 @@ message.confirm.delete.entity = Are you sure you want to delete {0} {1}{2}? caption.entity.assign.action.confirmbox = Confirm Assignment message.confirm.assign.entity = Are you sure you want to assign distribution {0} to {1} {2}? message.confirm.assign.multiple.entities = Are you sure you want to assign {0} {1} to distribution {2}? +message.confirm.assign.multiple.entities.multiple.distributions = Are you sure you want to assign {0} {1} to the distributions {2}? # character descriptions character.whitespace = whitespace From 11d946d544650b5de5938fe376d70515a4890781 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Wed, 13 Mar 2019 13:46:18 +0100 Subject: [PATCH 04/56] Misc fixes Signed-off-by: Stefan Behl --- .../jpa/JpaDeploymentManagement.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index 0b91551d8f..a90b562e5a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -527,6 +527,8 @@ private JpaAction closeActionIfSetWasAlreadyAssigned(final JpaAction action) { && action.getDistributionSet().getId().equals(target.getAssignedDistributionSet().getId())) { // the target has already the distribution set assigned, we don't // need to start the scheduled action, just finish it. + LOG.debug("Target {} has distribution set {} assigned. Closing action...", target.getControllerId(), + action.getDistributionSet().getName()); action.setStatus(Status.FINISHED); action.setActive(false); setSkipActionStatus(action); @@ -541,15 +543,19 @@ private JpaAction startScheduledActionIfNoCancelationHasToBeHandledFirst(final J // check if we need to override running update actions final List overrideObsoleteUpdateActions; - if (systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement - .getConfigurationValue(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, Boolean.class) - .getValue())) { - overrideObsoleteUpdateActions = Collections.emptyList(); - onlineDsAssignmentStrategy - .closeObsoleteUpdateActions(Collections.singletonList(action.getTarget().getId())); + if (!isMultiAssignmentsEnabled()) { + if (systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement + .getConfigurationValue(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, Boolean.class) + .getValue())) { + overrideObsoleteUpdateActions = Collections.emptyList(); + onlineDsAssignmentStrategy + .closeObsoleteUpdateActions(Collections.singletonList(action.getTarget().getId())); + } else { + overrideObsoleteUpdateActions = onlineDsAssignmentStrategy + .overrideObsoleteUpdateActions(Collections.singletonList(action.getTarget().getId())); + } } else { - overrideObsoleteUpdateActions = onlineDsAssignmentStrategy - .overrideObsoleteUpdateActions(Collections.singletonList(action.getTarget().getId())); + overrideObsoleteUpdateActions = Collections.emptyList(); } action.setActive(true); From aaa1558131e8bfa78f6999eab0e6ae98ce814318 Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Fri, 15 Mar 2019 11:29:11 +0100 Subject: [PATCH 05/56] test that previous assignments are not canceled Signed-off-by: Stefan Klotz --- .../jpa/DeploymentManagementTest.java | 59 ++++++++++++++----- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java index ddb3364068..ad3b039dba 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java @@ -514,26 +514,14 @@ public void assignDistributionSetAndAutoCloseActiveActions() { final DistributionSet ds1 = testdataFactory.createDistributionSet("1"); assignDistributionSet(ds1, targets); - List assignmentOne = actionRepository.findByDistributionSetId(PAGE, ds1.getId()).getContent(); - assertThat(assignmentOne).hasSize(10).as("Is active").allMatch(Action::isActive) - .as("Is assigned to DS " + ds1.getId()) - .allMatch(action -> action.getDistributionSet().getId().equals(ds1.getId())).as("Is running") - .allMatch(action -> action.getStatus() == Status.RUNNING); + assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), true, Status.RUNNING); // Second assignment final DistributionSet ds2 = testdataFactory.createDistributionSet("2"); assignDistributionSet(ds2, targets); - final List assignmentTwo = actionRepository.findByDistributionSetId(PAGE, ds2.getId()).getContent(); - assignmentOne = actionRepository.findByDistributionSetId(PAGE, ds1.getId()).getContent(); - assertThat(assignmentTwo).hasSize(10).as("Is active").allMatch(Action::isActive) - .as("Is assigned to DS " + ds2.getId()) - .allMatch(action -> action.getDistributionSet().getId().equals(ds2.getId())).as("Is running") - .allMatch(action -> action.getStatus() == Status.RUNNING); - assertThat(assignmentOne).hasSize(10).as("Is active").allMatch(action -> !action.isActive()) - .as("Is assigned to DS " + ds1.getId()) - .allMatch(action -> action.getDistributionSet().getId().equals(ds1.getId())).as("Is cancelled") - .allMatch(action -> action.getStatus() == Status.CANCELED); + assertDsExclusivelyAssignedToTargets(targets, ds2.getId(), true, Status.RUNNING); + assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), false, Status.CANCELED); assertThat(targetManagement.findByAssignedDistributionSet(PAGE, ds2.getId()).getContent()).hasSize(10) .as("InstallationDate not set").allMatch(target -> (target.getInstallationDate() == null)); @@ -544,6 +532,42 @@ public void assignDistributionSetAndAutoCloseActiveActions() { } } + @Test + public void previousAssignmentsAreNotCanceledInMultiAssignMode() { + setMultiAssignmentsEnabled(true); + try { + final List targets = testdataFactory.createTargets(10); + + // First assignment + final DistributionSet ds1 = testdataFactory.createDistributionSet("Multi-assign-1"); + assignDistributionSet(ds1, targets); + + assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), true, Status.RUNNING); + + // Second assignment + final DistributionSet ds2 = testdataFactory.createDistributionSet("Multi-assign-2"); + assignDistributionSet(ds2, targets); + + assertDsExclusivelyAssignedToTargets(targets, ds2.getId(), true, Status.RUNNING); + assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), true, Status.RUNNING); + + } finally { + setMultiAssignmentsEnabled(false); + } + } + + private void assertDsExclusivelyAssignedToTargets(final List targets, final long dsId, final boolean active, + final Status status) { + final List assignment = actionRepository.findByDistributionSetId(PAGE, dsId).getContent(); + + assertThat(assignment).hasSize(10).as("Active = " + active).allMatch(action -> action.isActive() == active) + .as("Is assigned to DS " + dsId).allMatch(action -> action.getDistributionSet().getId().equals(dsId)) + .as("State is " + status).allMatch(action -> action.getStatus() == status); + final long[] targetIds = targets.stream().mapToLong(Target::getId).toArray(); + assertThat(targetIds).as("All targets represented in assignment").containsExactlyInAnyOrder( + assignment.stream().mapToLong(action -> action.getTarget().getId()).toArray()); + } + /** * test a simple deployment by calling the * {@link TargetRepository#assignDistributionSet(DistributionSet, Iterable)} @@ -1211,4 +1235,9 @@ public void onApplicationEvent(final CancelTargetAssignmentEvent event) { } } + private void setMultiAssignmentsEnabled(final boolean enable) { + tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, + enable); + } + } From 22b056786035bd98e052611e92e38873e028da50 Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Fri, 15 Mar 2019 12:05:00 +0100 Subject: [PATCH 06/56] add description and expected events to test Signed-off-by: Stefan Klotz --- .../hawkbit/repository/jpa/DeploymentManagementTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java index ad3b039dba..4eb5882898 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java @@ -533,6 +533,12 @@ public void assignDistributionSetAndAutoCloseActiveActions() { } @Test + @Description("Verifies that if multi assignment is enabled previous Distribution is not canceled when a new one is assigned.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 10), + @Expect(type = TargetUpdatedEvent.class, count = 20), @Expect(type = ActionCreatedEvent.class, count = 20), + @Expect(type = DistributionSetCreatedEvent.class, count = 2), + @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), + @Expect(type = TargetAssignDistributionSetEvent.class, count = 2) }) public void previousAssignmentsAreNotCanceledInMultiAssignMode() { setMultiAssignmentsEnabled(true); try { From 46bbbd876d053ebf684ae21c6a2e34fa538fec1b Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Mon, 25 Mar 2019 09:58:31 +0100 Subject: [PATCH 07/56] extend TenantConfigurationManagement by NullPointerException proof getConfigurationValue method Signed-off-by: Stefan Klotz --- .../TenantConfigurationManagement.java | 30 +++++++++++++++++++ .../jpa/JpaDeploymentManagement.java | 9 ++---- .../jpa/JpaTenantConfigurationManagement.java | 7 +++++ 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TenantConfigurationManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TenantConfigurationManagement.java index 7dc73d971f..ad81fbea8b 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TenantConfigurationManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TenantConfigurationManagement.java @@ -118,6 +118,36 @@ TenantConfigurationValue buildTenantConfigurationVal TenantConfigurationValue getConfigurationValue(String configurationKeyName, Class propertyType); + /** + * Retrieves a configuration value from the e.g. tenant overwritten + * configuration values or in case the tenant does not a have a specific + * configuration the global default value hold in the {@link Environment} or + * the {@code defaultValue} if no global default is set. + * + * @param + * the type of the configuration value + * @param configurationKeyName + * the key of the configuration + * @param propertyType + * the type of the configuration value, e.g. {@code String.class} + * , {@code Integer.class}, etc + * @param defaultValue + * the value to return in case no configuration is found + * @return the converted configuration value either from the tenant specific + * configuration stored or from the fallback default values or + * {@code defaultValue} in case key has not been configured and no + * default value exists + * @throws TenantConfigurationValidatorException + * if the {@code propertyType} and the value in general does not + * match the expected type and format defined by the Key + * @throws ConversionFailedException + * if the property cannot be converted to the given + * {@code propertyType} + */ + @PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION) + T getConfigurationValue(String configurationKeyName, Class propertyType, + final T defaultValue); + /** * returns the global configuration property either defined in the property * file or an default value otherwise. diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index a90b562e5a..e9ffa1cae3 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -67,7 +67,6 @@ import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetWithActionType; -import org.eclipse.hawkbit.repository.model.TenantConfigurationValue; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.tenancy.TenantAware; @@ -808,11 +807,7 @@ private boolean isMultiAssignmentsEnabled() { private T getConfigValue(final String key, final Class valueType, final T defaultValue) { - return systemSecurityContext.runAsSystem(() -> { - final TenantConfigurationValue configEntry = tenantConfigurationManagement.getConfigurationValue(key, - valueType); - return configEntry != null ? configEntry.getValue() : defaultValue; - }); + return systemSecurityContext + .runAsSystem(() -> tenantConfigurationManagement.getConfigurationValue(key, valueType, defaultValue)); } - } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java index 80a7f6f99d..0249af8564 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java @@ -63,6 +63,13 @@ public TenantConfigurationValue getConfigurationValu return buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration); } + @Override + public T getConfigurationValue(final String configurationKeyName, + final Class propertyType, final T defaultValue) { + final TenantConfigurationValue configEntry = getConfigurationValue(configurationKeyName, propertyType); + return configEntry != null ? configEntry.getValue() : defaultValue; + } + /** * Validates the data type of the tenant configuration. If it is possible to * cast to the given data type. From c6f763f5e39009cc12de864e5b2a4d1c9934458f Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Mon, 25 Mar 2019 14:02:13 +0100 Subject: [PATCH 08/56] Hide "Required Migration Step" if Multi Assignments is enabled Signed-off-by: Stefan Klotz --- .../AbstractDistributionSetDetails.java | 22 +++++++++++++++---- .../ui/distributions/DistributionsView.java | 6 +++-- .../dstable/DistributionSetDetails.java | 6 +++-- .../dstable/DistributionSetTableLayout.java | 7 +++--- .../DistributionAddUpdateWindowLayout.java | 19 +++++++++++++--- .../dstable/DistributionDetails.java | 7 ++++-- .../dstable/DistributionTableLayout.java | 7 +++--- 7 files changed, 54 insertions(+), 20 deletions(-) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/AbstractDistributionSetDetails.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/AbstractDistributionSetDetails.java index d741641503..c60608d491 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/AbstractDistributionSetDetails.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/AbstractDistributionSetDetails.java @@ -12,7 +12,9 @@ import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.DistributionSetTagManagement; +import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; import org.eclipse.hawkbit.ui.SpPermissionChecker; import org.eclipse.hawkbit.ui.common.tagdetails.DistributionTagToken; import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; @@ -56,6 +58,8 @@ public abstract class AbstractDistributionSetDetails private final SoftwareModuleDetailsTable softwareModuleDetailsTable; + private final transient TenantConfigurationManagement tenantConfigurationManagement; + private VerticalLayout softwareModuleTab; protected AbstractDistributionSetDetails(final VaadinMessageSource i18n, final UIEventBus eventBus, @@ -64,7 +68,8 @@ protected AbstractDistributionSetDetails(final VaadinMessageSource i18n, final U final DistributionSetManagement distributionSetManagement, final DsMetadataPopupLayout dsMetadataPopupLayout, final UINotification uiNotification, final DistributionSetTagManagement distributionSetTagManagement, - final SoftwareModuleDetailsTable softwareModuleDetailsTable) { + final SoftwareModuleDetailsTable softwareModuleDetailsTable, + final TenantConfigurationManagement tenantConfigurationManagement) { super(i18n, eventBus, permissionChecker, managementUIState); this.distributionAddUpdateWindowLayout = distributionAddUpdateWindowLayout; this.uiNotification = uiNotification; @@ -73,6 +78,7 @@ protected AbstractDistributionSetDetails(final VaadinMessageSource i18n, final U this.distributionTagToken = new DistributionTagToken(permissionChecker, i18n, uiNotification, eventBus, managementUIState, distributionSetTagManagement, distributionSetManagement); this.softwareModuleDetailsTable = softwareModuleDetailsTable; + this.tenantConfigurationManagement = tenantConfigurationManagement; dsMetadataTable = new DistributionSetMetadataDetailsLayout(i18n, distributionSetManagement, dsMetadataPopupLayout); @@ -175,9 +181,17 @@ private void updateDistributionSetDetailsLayout(final String type, final Boolean typeLabel.setId(UIComponentIdProvider.DETAILS_TYPE_LABEL_ID); detailsTabLayout.addComponent(typeLabel); - detailsTabLayout.addComponent( - SPUIComponentProvider.createNameValueLabel(getI18n().getMessage("checkbox.dist.migration.required"), - getMigrationRequiredValue(isMigrationRequired))); + if (!isMultiAssignmentEnabled()) { + detailsTabLayout.addComponent( + SPUIComponentProvider.createNameValueLabel(getI18n().getMessage("checkbox.dist.migration.required"), + getMigrationRequiredValue(isMigrationRequired))); + } + + } + + private boolean isMultiAssignmentEnabled() { + return tenantConfigurationManagement.getConfigurationValue(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, + Boolean.class, false); } private String getMigrationRequiredValue(final Boolean isMigrationRequired) { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/DistributionsView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/DistributionsView.java index 4421560310..45509c96e4 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/DistributionsView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/DistributionsView.java @@ -21,6 +21,7 @@ import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement; import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.TargetManagement; +import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.ui.AbstractHawkbitUI; import org.eclipse.hawkbit.ui.SpPermissionChecker; import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent; @@ -102,7 +103,8 @@ public class DistributionsView extends AbstractNotificationView implements Brows final DistributionsViewClientCriterion distributionsViewClientCriterion, final ArtifactUploadState artifactUploadState, final SystemManagement systemManagement, final ArtifactManagement artifactManagement, final NotificationUnreadButton notificationUnreadButton, - final DistributionsViewMenuItem distributionsViewMenuItem) { + final DistributionsViewMenuItem distributionsViewMenuItem, + final TenantConfigurationManagement configManagement) { super(eventBus, notificationUnreadButton); this.permChecker = permChecker; this.i18n = i18n; @@ -117,7 +119,7 @@ public class DistributionsView extends AbstractNotificationView implements Brows this.distributionTableLayout = new DistributionSetTableLayout(i18n, eventBus, permChecker, manageDistUIState, softwareModuleManagement, distributionSetManagement, distributionSetTypeManagement, targetManagement, entityFactory, uiNotification, distributionSetTagManagement, distributionsViewClientCriterion, - systemManagement); + systemManagement, configManagement); this.softwareModuleTableLayout = new SwModuleTableLayout(i18n, uiNotification, eventBus, softwareModuleManagement, softwareModuleTypeManagement, entityFactory, manageDistUIState, permChecker, distributionsViewClientCriterion, artifactUploadState, artifactManagement); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java index 625d48fde7..ff5427d2a6 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetDetails.java @@ -13,6 +13,7 @@ import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.DistributionSetTagManagement; +import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.ui.SpPermissionChecker; import org.eclipse.hawkbit.ui.artifacts.event.SoftwareModuleEvent; @@ -58,11 +59,12 @@ public class DistributionSetDetails extends AbstractDistributionSetDetails { final DistributionAddUpdateWindowLayout distributionAddUpdateWindowLayout, final DistributionSetManagement distributionSetManagement, final UINotification uiNotification, final DistributionSetTagManagement distributionSetTagManagement, - final DsMetadataPopupLayout dsMetadataPopupLayout) { + final DsMetadataPopupLayout dsMetadataPopupLayout, final TenantConfigurationManagement configManagement) { super(i18n, eventBus, permissionChecker, managementUIState, distributionAddUpdateWindowLayout, distributionSetManagement, dsMetadataPopupLayout, uiNotification, distributionSetTagManagement, createSoftwareModuleDetailsTable(i18n, permissionChecker, distributionSetManagement, eventBus, - manageDistUIState, uiNotification)); + manageDistUIState, uiNotification), + configManagement); this.manageDistUIState = manageDistUIState; tfqDetailsTable = new TargetFilterQueryDetailsTable(i18n); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTableLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTableLayout.java index 3f18493114..e6fa68df1a 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTableLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/distributions/dstable/DistributionSetTableLayout.java @@ -15,6 +15,7 @@ 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.ui.SpPermissionChecker; import org.eclipse.hawkbit.ui.common.table.AbstractTableLayout; import org.eclipse.hawkbit.ui.dd.criteria.DistributionsViewClientCriterion; @@ -41,7 +42,7 @@ public DistributionSetTableLayout(final VaadinMessageSource i18n, final UIEventB final EntityFactory entityFactory, final UINotification uiNotification, final DistributionSetTagManagement distributionSetTagManagement, final DistributionsViewClientCriterion distributionsViewClientCriterion, - final SystemManagement systemManagement) { + final SystemManagement systemManagement, final TenantConfigurationManagement configManagement) { this.distributionSetTable = new DistributionSetTable(eventBus, i18n, uiNotification, permissionChecker, manageDistUIState, distributionSetManagement, softwareManagement, distributionsViewClientCriterion, @@ -49,7 +50,7 @@ public DistributionSetTableLayout(final VaadinMessageSource i18n, final UIEventB final DistributionAddUpdateWindowLayout distributionAddUpdateWindowLayout = new DistributionAddUpdateWindowLayout( i18n, uiNotification, eventBus, distributionSetManagement, distributionSetTypeManagement, - systemManagement, entityFactory, distributionSetTable); + systemManagement, entityFactory, distributionSetTable, configManagement); final DsMetadataPopupLayout popupLayout = new DsMetadataPopupLayout(i18n, uiNotification, eventBus, distributionSetManagement, entityFactory, permissionChecker); @@ -60,7 +61,7 @@ public DistributionSetTableLayout(final VaadinMessageSource i18n, final UIEventB distributionSetTable, new DistributionSetDetails(i18n, eventBus, permissionChecker, manageDistUIState, null, distributionAddUpdateWindowLayout, distributionSetManagement, uiNotification, - distributionSetTagManagement, popupLayout)); + distributionSetTagManagement, popupLayout, configManagement)); } public DistributionSetTable getDistributionSetTable() { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java index 04cc16160f..3e66d4e38d 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java @@ -15,10 +15,12 @@ import org.eclipse.hawkbit.repository.DistributionSetTypeManagement; import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.SystemManagement; +import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.TenantMetaData; +import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; import org.eclipse.hawkbit.ui.common.CommonDialogWindow; import org.eclipse.hawkbit.ui.common.CommonDialogWindow.SaveDialogCloseListener; import org.eclipse.hawkbit.ui.common.DistributionSetTypeBeanQuery; @@ -66,6 +68,7 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent { private final transient EntityFactory entityFactory; private final DistributionSetTable distributionSetTable; + private final TenantConfigurationManagement tenantConfigurationManagement; private TextField distNameTextField; private TextField distVersionTextField; @@ -94,11 +97,14 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent { * EntityFactory * @param distributionSetTable * DistributionSetTable + * @param tenantConfigurationManagement + * TenantConfigurationManagement */ public DistributionAddUpdateWindowLayout(final VaadinMessageSource i18n, final UINotification notificationMessage, final UIEventBus eventBus, final DistributionSetManagement distributionSetManagement, final DistributionSetTypeManagement distributionSetTypeManagement, final SystemManagement systemManagement, - final EntityFactory entityFactory, final DistributionSetTable distributionSetTable) { + final EntityFactory entityFactory, final DistributionSetTable distributionSetTable, + final TenantConfigurationManagement tenantConfigurationManagement) { this.i18n = i18n; this.notificationMessage = notificationMessage; this.eventBus = eventBus; @@ -107,6 +113,7 @@ public DistributionAddUpdateWindowLayout(final VaadinMessageSource i18n, final U this.systemManagement = systemManagement; this.entityFactory = entityFactory; this.distributionSetTable = distributionSetTable; + this.tenantConfigurationManagement = tenantConfigurationManagement; createRequiredComponents(); buildLayout(); } @@ -134,8 +141,8 @@ public void saveOrUpdate() { final DistributionSet currentDS = distributionSetManagement.update(entityFactory.distributionSet() .update(editDistId).name(distNameTextField.getValue()).description(descTextArea.getValue()) .version(distVersionTextField.getValue()).requiredMigrationStep(isMigStepReq)); - notificationMessage.displaySuccess(i18n.getMessage("message.new.dist.save.success", - new Object[] { currentDS.getName(), currentDS.getVersion() })); + notificationMessage.displaySuccess( + i18n.getMessage("message.new.dist.save.success", currentDS.getName(), currentDS.getVersion())); // update table row+details layout eventBus.publish(this, new DistributionTableEvent(BaseEntityEventType.UPDATED_ENTITY, currentDS)); }); @@ -283,6 +290,12 @@ public void resetComponents() { distsetTypeNameComboBox.setEnabled(true); descTextArea.clear(); reqMigStepCheckbox.clear(); + reqMigStepCheckbox.setVisible(!isMultiAssignmentEnabled()); + } + + private boolean isMultiAssignmentEnabled() { + return tenantConfigurationManagement.getConfigurationValue(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, + Boolean.class, false); } private void populateValuesOfDistribution(final Long editDistId) { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionDetails.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionDetails.java index 050eb94d29..a745b34d96 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionDetails.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionDetails.java @@ -10,6 +10,7 @@ import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.DistributionSetTagManagement; +import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.ui.SpPermissionChecker; import org.eclipse.hawkbit.ui.common.detailslayout.AbstractDistributionSetDetails; import org.eclipse.hawkbit.ui.common.detailslayout.SoftwareModuleDetailsTable; @@ -31,10 +32,12 @@ public class DistributionDetails extends AbstractDistributionSetDetails { final DistributionSetManagement distributionSetManagement, final DsMetadataPopupLayout dsMetadataPopupLayout, final UINotification uiNotification, final DistributionSetTagManagement distributionSetTagManagement, - final DistributionAddUpdateWindowLayout distributionAddUpdateWindowLayout) { + final DistributionAddUpdateWindowLayout distributionAddUpdateWindowLayout, + final TenantConfigurationManagement tenantConfigurationManagement) { super(i18n, eventBus, permissionChecker, managementUIState, distributionAddUpdateWindowLayout, distributionSetManagement, dsMetadataPopupLayout, uiNotification, distributionSetTagManagement, - createSoftwareModuleDetailsTable(i18n, permissionChecker, uiNotification)); + createSoftwareModuleDetailsTable(i18n, permissionChecker, uiNotification), + tenantConfigurationManagement); restoreState(); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTableLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTableLayout.java index b7e403228e..fbb1d48cf3 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTableLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTableLayout.java @@ -44,12 +44,11 @@ public DistributionTableLayout(final VaadinMessageSource i18n, final UIEventBus final UINotification notification, final DistributionSetTagManagement distributionSetTagManagement, final TargetTagManagement targetTagManagement, final SystemManagement systemManagement, final TargetManagement targetManagement, final DeploymentManagement deploymentManagement, - final TenantConfigurationManagement configManagement, - final UiProperties uiProperties) { + final TenantConfigurationManagement configManagement, final UiProperties uiProperties) { final DistributionAddUpdateWindowLayout distributionAddUpdateWindowLayout = new DistributionAddUpdateWindowLayout( i18n, notification, eventBus, distributionSetManagement, distributionSetTypeManagement, - systemManagement, entityFactory, null); + systemManagement, entityFactory, null, configManagement); final DsMetadataPopupLayout dsMetadataPopupLayout = new DsMetadataPopupLayout(i18n, notification, eventBus, distributionSetManagement, entityFactory, permissionChecker); @@ -62,7 +61,7 @@ public DistributionTableLayout(final VaadinMessageSource i18n, final UIEventBus distributionTable, new DistributionDetails(i18n, eventBus, permissionChecker, managementUIState, distributionSetManagement, dsMetadataPopupLayout, notification, distributionSetTagManagement, - distributionAddUpdateWindowLayout)); + distributionAddUpdateWindowLayout, configManagement)); } public DistributionTable getDistributionTable() { From a6641875e69da235bde1ca37034a2f03c46b93b4 Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Mon, 25 Mar 2019 15:48:26 +0100 Subject: [PATCH 09/56] Make fields transient Signed-off-by: Stefan Klotz --- .../management/dstable/DistributionAddUpdateWindowLayout.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java index 3e66d4e38d..7e57314129 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java @@ -66,9 +66,9 @@ public class DistributionAddUpdateWindowLayout extends CustomComponent { private final transient DistributionSetTypeManagement distributionSetTypeManagement; private final transient SystemManagement systemManagement; private final transient EntityFactory entityFactory; + private final transient TenantConfigurationManagement tenantConfigurationManagement; private final DistributionSetTable distributionSetTable; - private final TenantConfigurationManagement tenantConfigurationManagement; private TextField distNameTextField; private TextField distVersionTextField; From b4d7a80e13f2bb886102622401f65c7645ec5c47 Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Wed, 27 Mar 2019 15:09:59 +0100 Subject: [PATCH 10/56] Add IDs for Required Migration Step elements Signed-off-by: Stefan Klotz --- .../detailslayout/AbstractDistributionSetDetails.java | 8 +++++--- .../dstable/DistributionAddUpdateWindowLayout.java | 3 ++- .../hawkbit/ui/utils/UIComponentIdProvider.java | 10 ++++++++++ hawkbit-ui/src/main/resources/messages.properties | 1 - 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/AbstractDistributionSetDetails.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/AbstractDistributionSetDetails.java index c60608d491..2df2e4d528 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/AbstractDistributionSetDetails.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/AbstractDistributionSetDetails.java @@ -181,10 +181,12 @@ private void updateDistributionSetDetailsLayout(final String type, final Boolean typeLabel.setId(UIComponentIdProvider.DETAILS_TYPE_LABEL_ID); detailsTabLayout.addComponent(typeLabel); + final Label requiredMigrationStepLabel = SPUIComponentProvider.createNameValueLabel( + getI18n().getMessage("checkbox.dist.migration.required"), + getMigrationRequiredValue(isMigrationRequired)); + requiredMigrationStepLabel.setId(UIComponentIdProvider.DETAILS_REQUIRED_MIGRATION_STEP_LABEL_ID); if (!isMultiAssignmentEnabled()) { - detailsTabLayout.addComponent( - SPUIComponentProvider.createNameValueLabel(getI18n().getMessage("checkbox.dist.migration.required"), - getMigrationRequiredValue(isMigrationRequired))); + detailsTabLayout.addComponent(requiredMigrationStepLabel); } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java index 7e57314129..1ae4d50013 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java @@ -364,7 +364,8 @@ private CommonDialogWindow getWindow(final Long editDistId) { populateValuesOfDistribution(editDistId); } - return new WindowBuilder(SPUIDefinitions.CREATE_UPDATE_WINDOW).caption(caption).content(this).layout(formLayout) + return new WindowBuilder(SPUIDefinitions.CREATE_UPDATE_WINDOW).caption(caption).content(this) + .id(UIComponentIdProvider.CREATE_POPUP_ID).layout(formLayout) .i18n(i18n).saveDialogCloseListener(saveDialogCloseListener).buildCommonDialogWindow(); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java index 5d96aa0fd7..f4c7846b3d 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/UIComponentIdProvider.java @@ -604,6 +604,11 @@ public final class UIComponentIdProvider { */ public static final String DETAILS_TYPE_LABEL_ID = "details.type"; + /** + * Table details Required Migration Step label id. + */ + public static final String DETAILS_REQUIRED_MIGRATION_STEP_LABEL_ID = "details.required.migration.step"; + /** * Id of show filter button in software module table. */ @@ -1145,6 +1150,11 @@ public final class UIComponentIdProvider { */ public static final String METADATA_POPUP_ID = "metadata.popup.id"; + /** + * Create popup id. + */ + public static final String CREATE_POPUP_ID = "create.popup.id"; + /** * DistributionSet table details tab id in Distributions . */ diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index 8aebfa3ba1..acfa9b1c83 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -70,7 +70,6 @@ header.dist.twintable.available=Available header.target.installed = Installed header.target.assigned = Assigned header.type=Type -header.migrations.step=IsRequiredMigrationStep header.action=Actions header.action.run=Run header.action.approve=Approve From 7573b7bdc1f29d29fa7220d4480f1e92a19d5b33 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Fri, 29 Mar 2019 15:27:35 +0100 Subject: [PATCH 11/56] Save work in progress Signed-off-by: Stefan Behl --- .../AmqpMessageDispatcherServiceTest.java | 2 +- .../AbstractAmqpServiceIntegrationTest.java | 2 +- ...ssageDispatcherServiceIntegrationTest.java | 8 +-- ...pMessageHandlerServiceIntegrationTest.java | 6 +-- .../repository/DeploymentManagement.java | 28 ++++++++++ .../DistributionSetAssignmentResult.java | 27 ++++++++-- .../DistributionSetAssignmentResultMap.java | 28 ++++++++++ .../jpa/AbstractDsAssignmentStrategy.java | 41 ++++++++++----- .../jpa/JpaDeploymentManagement.java | 52 +++++++++++-------- .../jpa/OfflineDsAssignmentStrategy.java | 23 ++++++-- .../jpa/OnlineDsAssignmentStrategy.java | 35 +++++++++---- .../jpa/ControllerManagementTest.java | 8 +-- .../jpa/DeploymentManagementTest.java | 12 ++--- .../repository/jpa/RolloutManagementTest.java | 4 +- .../jpa/TargetManagementSearchTest.java | 4 +- .../repository/jpa/TargetManagementTest.java | 2 +- .../jpa/autoassign/AutoAssignCheckerTest.java | 2 +- .../autocleanup/AutoActionCleanupTest.java | 20 +++---- .../rest/resource/DdiCancelActionTest.java | 23 ++++---- .../rest/resource/DdiDeploymentBaseTest.java | 10 ++-- .../rest/resource/MgmtTargetResourceTest.java | 2 +- .../RootControllerDocumentationTest.java | 10 ++-- .../TargetResourceDocumentationTest.java | 2 +- 23 files changed, 238 insertions(+), 113 deletions(-) create mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java index b9db0f2d61..e292210e49 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java @@ -125,7 +125,7 @@ private Message getCaptureAdressEvent(final TargetAssignDistributionSetEvent tar } 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 diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java index 21d50b68ff..bc194d4e00 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java @@ -228,7 +228,7 @@ protected Long cancelAction(final Long actionId, final String controllerId) { 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) { diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java index be8da4674d..9ae2573d8d 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java @@ -122,11 +122,11 @@ public void assignDistributionSetMultipleTimes() { 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); waitUntilTargetHasStatus(controllerId, TargetUpdateStatus.PENDING); - assertCancelActionMessage(assignmentResult.getActions().get(0), controllerId); + assertCancelActionMessage(assignmentResult.getActionIds().get(0), controllerId); } @@ -179,7 +179,7 @@ public void attributeRequestAfterSuccessfulUpdate() { final Target target = controllerManagement.getByControllerId(controllerId).get(); final DistributionSet distributionSet = testdataFactory.createDistributionSet(UUID.randomUUID().toString()); - final long actionId1 = assignDistributionSet(distributionSet, target).getActions().get(0); + final long actionId1 = assignDistributionSet(distributionSet, target).getActionIds().get(0); waitUntilTargetHasStatus(controllerId, TargetUpdateStatus.PENDING); final Message messageError = createActionStatusUpdateMessage(controllerId, TENANT_EXIST, actionId1, DmfActionStatus.ERROR); @@ -188,7 +188,7 @@ public void attributeRequestAfterSuccessfulUpdate() { assertRequestAttributesUpdateMessageAbsent(); - final long actionId2 = assignDistributionSet(distributionSet, target).getActions().get(0); + final long actionId2 = assignDistributionSet(distributionSet, target).getActionIds().get(0); waitUntilTargetHasStatus(controllerId, TargetUpdateStatus.PENDING); final Message messageFin = createActionStatusUpdateMessage(controllerId, TENANT_EXIST, actionId2, DmfActionStatus.FINISHED); diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageHandlerServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageHandlerServiceIntegrationTest.java index 3716c0932f..375edd9380 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageHandlerServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageHandlerServiceIntegrationTest.java @@ -563,13 +563,13 @@ public void receiveCancelUpdateMessageAfterAssignmentWasCanceled() { 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()); } @@ -807,7 +807,7 @@ private void sendUpdateAttributesMessageWithGivenAttributes(final String target, 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; } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java index 7fbb989726..ace9614503 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java @@ -30,6 +30,7 @@ import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; +import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResultMap; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.Target; @@ -104,6 +105,33 @@ DistributionSetAssignmentResult assignDistributionSet(long dsID, @NotNull Action DistributionSetAssignmentResult assignDistributionSet(long dsID, @NotEmpty Collection targets); + /** + * Assigns the given set of {@link DistributionSet} entities to the given + * {@link Target}s using the specified {@link ActionType} and + * {@code forcetime}. + * + * @param dsIDs + * the set of IDs of the distribution sets to assign + * @param targets + * a list of all targets and their action type + * @return the assignment result map + * + * @throws IncompleteDistributionSetException + * if mandatory {@link SoftwareModuleType} are not assigned as + * defined by the {@link DistributionSetType}. + * + * @throws EntityNotFoundException + * if either provided {@link DistributionSet} or {@link Target}s + * do not exist + * + * @throws QuotaExceededException + * if the maximum number of targets the distribution set can be + * assigned to at once is exceeded + */ + @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) + DistributionSetAssignmentResultMap assignDistributionSets(@NotEmpty Set dsIDs, + @NotEmpty Collection targets); + /** * Assigns the addressed {@link DistributionSet} to all {@link Target}s by * their IDs with a specific {@link ActionType} and an action message. diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResult.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResult.java index 350373daf9..b6f2d1a4ec 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResult.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResult.java @@ -26,6 +26,8 @@ public class DistributionSetAssignmentResult extends AssignmentResult { private final List assignedTargets; private final List actions; + private final DistributionSet distributionSet; + private final TargetManagement targetManagement; /** @@ -45,31 +47,48 @@ public class DistributionSetAssignmentResult extends AssignmentResult { * of the assignment * */ - public DistributionSetAssignmentResult(final List assignedTargets, final int assigned, + public DistributionSetAssignmentResult(final DistributionSet distributionSet, final List assignedTargets, + final int assigned, final int alreadyAssigned, final List actions, final TargetManagement targetManagement) { super(assigned, alreadyAssigned, 0, Collections.emptyList(), Collections.emptyList()); + this.distributionSet = distributionSet; this.assignedTargets = assignedTargets; this.actions = actions; this.targetManagement = targetManagement; } + /** + * @return The distribution set that has been assigned + */ + public DistributionSet getDistributionSet() { + return distributionSet; + } + /** * @return the actionIds */ - public List getActions() { + public List getActionIds() { if (actions == null) { return Collections.emptyList(); } - return actions.stream().map(Action::getId).collect(Collectors.toList()); } + /** + * @return the actions + */ + public List getActions() { + if (actions == null) { + return Collections.emptyList(); + } + return actions; + } + @Override public List getAssignedEntity() { if (CollectionUtils.isEmpty(assignedTargets)) { return Collections.emptyList(); } - return targetManagement.getByControllerID(assignedTargets); } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java new file mode 100644 index 0000000000..7f47aa3597 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java @@ -0,0 +1,28 @@ +package org.eclipse.hawkbit.repository.model; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +public class DistributionSetAssignmentResultMap { + + private final Map results = new HashMap<>(); + + public Set distributionSetIds() { + return results.keySet(); + } + + public void putResult(final long dsId, final DistributionSetAssignmentResult result) { + results.put(dsId, result); + } + + public DistributionSetAssignmentResult getResult(final long dsId) { + return results.get(dsId); + } + + public Set> resultSet() { + return results.entrySet(); + } + +} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java index a88c9adbd5..0d7de08370 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java @@ -30,6 +30,8 @@ import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; +import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResultMap; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.slf4j.Logger; @@ -79,6 +81,13 @@ public abstract class AbstractDsAssignmentStrategy { */ abstract List findTargetsForAssignment(final List controllerIDs, final long distributionSetId); + /** + * + * @param set + * @param targets + */ + abstract void sendTargetUpdatedEvents(final DistributionSet set, final List targets); + /** * Update status and DS fields of given target. * @@ -89,8 +98,8 @@ public abstract class AbstractDsAssignmentStrategy { * @param currentUser * for auditing */ - abstract void updateTargetStatus(final JpaDistributionSet distributionSet, final List> targetIds, - final String currentUser); + abstract void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet distributionSet, + final List> targetIds, final String currentUser); /** * Cancels actions that can be canceled (i.e. @@ -117,20 +126,24 @@ abstract void updateTargetStatus(final JpaDistributionSet distributionSet, final abstract void closeActiveActions(List> targetIds); /** - * Handles event sending related to the assignment. + * Sends the assignment events for the given + * {@link DistributionSetAssignmentResult}. * - * @param set - * that has been assigned - * @param targets - * to send events for - * @param targetIdsCancelList - * targets where an action was canceled - * @param controllerIdsToActions - * mapping of {@link Target#getControllerId()} to new - * {@link Action} that was created as part of the assignment. + * @param assignmentResult + * the assignment result + */ + abstract DistributionSetAssignmentResult sendDistributionSetAssignedEvent( + final DistributionSetAssignmentResult assignmentResult); + + /** + * Sends the assignment events for the given + * {@link DistributionSetAssignmentResultMap}. + * + * @param assignmentResults + * the results of multiple assignments */ - abstract void sendAssignmentEvents(DistributionSet set, final List targets, - final Set targetIdsCancelList, final Map controllerIdsToActions); + abstract DistributionSetAssignmentResultMap sendDistributionSetsAssignedEvent( + final DistributionSetAssignmentResultMap assignmentResult); protected void sendTargetAssignDistributionSetEvent(final String tenant, final long distributionSetId, final List actions) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index e9ffa1cae3..3a8dc0e89a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -62,6 +62,7 @@ import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; +import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResultMap; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.Target; @@ -177,11 +178,12 @@ protected JpaDeploymentManagement(final EntityManager entityManager, final Actio ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY)) public DistributionSetAssignmentResult offlineAssignedDistributionSet(final Long dsID, final Collection controllerIDs) { - return assignDistributionSetToTargets(dsID, + final DistributionSetAssignmentResult result = assignDistributionSetToTargets(dsID, controllerIDs.stream() .map(controllerId -> new TargetWithActionType(controllerId, ActionType.FORCED, -1)) .collect(Collectors.toList()), null, offlineDsAssignmentStrategy); + return offlineDsAssignmentStrategy.sendDistributionSetAssignedEvent(result); } @Override @@ -191,12 +193,12 @@ public DistributionSetAssignmentResult offlineAssignedDistributionSet(final Long public DistributionSetAssignmentResult assignDistributionSet(final long dsID, final ActionType actionType, final long forcedTimestamp, final Collection controllerIDs) { - return assignDistributionSetToTargets(dsID, + final DistributionSetAssignmentResult result = assignDistributionSetToTargets(dsID, controllerIDs.stream() .map(controllerId -> new TargetWithActionType(controllerId, actionType, forcedTimestamp)) .collect(Collectors.toList()), null, onlineDsAssignmentStrategy); - + return onlineDsAssignmentStrategy.sendDistributionSetAssignedEvent(result); } @Override @@ -209,6 +211,16 @@ public DistributionSetAssignmentResult assignDistributionSet(final long dsID, return assignDistributionSetToTargets(dsID, targets, null, onlineDsAssignmentStrategy); } + @Override + public DistributionSetAssignmentResultMap assignDistributionSets(final Set dsIDs, + final Collection targets) { + + final DistributionSetAssignmentResultMap results = new DistributionSetAssignmentResultMap(); + dsIDs.forEach(dsID -> results.putResult(dsID, + assignDistributionSetToTargets(dsID, targets, null, onlineDsAssignmentStrategy))); + return onlineDsAssignmentStrategy.sendDistributionSetsAssignedEvent(results); + } + @Override @Transactional(isolation = Isolation.READ_COMMITTED) @Retryable(include = { @@ -216,7 +228,9 @@ public DistributionSetAssignmentResult assignDistributionSet(final long dsID, public DistributionSetAssignmentResult assignDistributionSet(final long dsID, final Collection targets, final String actionMessage) { - return assignDistributionSetToTargets(dsID, targets, actionMessage, onlineDsAssignmentStrategy); + final DistributionSetAssignmentResult result = assignDistributionSetToTargets(dsID, targets, actionMessage, + onlineDsAssignmentStrategy); + return onlineDsAssignmentStrategy.sendDistributionSetAssignedEvent(result); } /** @@ -261,8 +275,8 @@ private DistributionSetAssignmentResult assignDistributionSetToTargets(final Lon // detaching as it is not necessary to persist the set itself entityManager.detach(distributionSetEntity); // return with nothing as all targets had the DS already assigned - return new DistributionSetAssignmentResult(Collections.emptyList(), 0, targetsWithActionType.size(), - Collections.emptyList(), targetManagement); + return new DistributionSetAssignmentResult(distributionSetEntity, Collections.emptyList(), 0, + targetsWithActionType.size(), Collections.emptyList(), targetManagement); } // split tIDs length into max entries in-statement because many database @@ -273,12 +287,7 @@ private DistributionSetAssignmentResult assignDistributionSetToTargets(final Lon targetEntities.stream().map(Target::getId).collect(Collectors.toList()), Constants.MAX_ENTRIES_IN_STATEMENT); - // override all active actions and set them into canceling state, we - // need to remember which one we have been switched to canceling state - // because for targets which we have changed to canceling we don't want - // to publish the new action update event. - final Set cancelingTargetEntitiesIds = closeOrCancelActiveActions(assignmentStrategy, - targetEntitiesIdsChunks); + closeOrCancelActiveActions(assignmentStrategy, targetEntitiesIdsChunks); // cancel all scheduled actions which are in-active, these actions were // not active before and the manual assignment which has been done // cancels them @@ -295,10 +304,9 @@ private DistributionSetAssignmentResult assignDistributionSetToTargets(final Lon // action history. createActionsStatus(controllerIdsToActions.values(), assignmentStrategy, actionMessage); - detachEntitiesAndSendAssignmentEvents(distributionSetEntity, targetEntities, assignmentStrategy, - cancelingTargetEntitiesIds, controllerIdsToActions); + detachEntitiesAndSendTargetUpdatedEvents(distributionSetEntity, targetEntities, assignmentStrategy); - return new DistributionSetAssignmentResult( + return new DistributionSetAssignmentResult(distributionSetEntity, targetEntities.stream().map(Target::getControllerId).collect(Collectors.toList()), targetEntities.size(), controllerIDs.size() - targetEntities.size(), Lists.newArrayList(controllerIdsToActions.values()), targetManagement); @@ -375,7 +383,7 @@ public void cancelInactiveScheduledActionsForTargets(final List targetIds) private void setAssignedDistributionSetAndTargetUpdateStatus(final AbstractDsAssignmentStrategy assignmentStrategy, final JpaDistributionSet set, final List> targetIdsChunks) { final String currentUser = auditorProvider.getCurrentAuditor().orElse(null); - assignmentStrategy.updateTargetStatus(set, targetIdsChunks, currentUser); + assignmentStrategy.setAssignedDistributionSetAndTargetStatus(set, targetIdsChunks, currentUser); } private Map createActions(final Collection targetsWithActionType, @@ -396,15 +404,14 @@ private void createActionsStatus(final Collection actions, .collect(Collectors.toList())); } - private void detachEntitiesAndSendAssignmentEvents(final JpaDistributionSet set, final List targets, - final AbstractDsAssignmentStrategy assignmentStrategy, final Set targetIdsCancellList, - final Map controllerIdsToActions) { + private void detachEntitiesAndSendTargetUpdatedEvents(final JpaDistributionSet set, final List targets, + final AbstractDsAssignmentStrategy assignmentStrategy) { // detaching as it is not necessary to persist the set itself entityManager.detach(set); // detaching as the entity has been updated by the JPQL query above targets.forEach(entityManager::detach); - - assignmentStrategy.sendAssignmentEvents(set, targets, targetIdsCancellList, controllerIdsToActions); + // send out TargetUpdated events + assignmentStrategy.sendTargetUpdatedEvents(set, targets); } @Override @@ -810,4 +817,5 @@ private T getConfigValue(final String key, final Class< return systemSecurityContext .runAsSystem(() -> tenantConfigurationManagement.getConfigurationValue(key, valueType, defaultValue)); } -} + +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java index af07c185fd..5db95548c2 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java @@ -27,6 +27,8 @@ import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications; import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.DistributionSet; +import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; +import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResultMap; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.springframework.cloud.bus.BusProperties; @@ -50,15 +52,27 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { } @Override - void sendAssignmentEvents(final DistributionSet set, final List targets, - final Set targetIdsCancellList, final Map targetIdsToActions) { - + void sendTargetUpdatedEvents(final DistributionSet set, final List targets) { targets.forEach(target -> { target.setUpdateStatus(TargetUpdateStatus.IN_SYNC); sendTargetUpdatedEvent(target); }); } + @Override + DistributionSetAssignmentResult sendDistributionSetAssignedEvent( + final DistributionSetAssignmentResult assignmentResult) { + // no need to send an event in the offline case + return assignmentResult; + } + + @Override + DistributionSetAssignmentResultMap sendDistributionSetsAssignedEvent( + final DistributionSetAssignmentResultMap assignmentResults) { + // no need to send an event in the offline case + return assignmentResults; + } + @Override public List findTargetsForAssignment(final List controllerIDs, final long setId) { return Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream() @@ -79,7 +93,8 @@ void closeActiveActions(final List> targetIds) { } @Override - void updateTargetStatus(final JpaDistributionSet set, final List> targetIds, final String currentUser) { + void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet set, final List> targetIds, + final String currentUser) { targetIds.forEach(tIds -> targetRepository.setAssignedAndInstalledDistributionSetAndUpdateStatus( TargetUpdateStatus.IN_SYNC, set, System.currentTimeMillis(), currentUser, tIds)); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java index 43b73f3dbc..a28d778d20 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java @@ -25,7 +25,8 @@ import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.DistributionSet; -import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; +import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResultMap; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.springframework.cloud.bus.BusProperties; @@ -48,19 +49,32 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { } @Override - void sendAssignmentEvents(final DistributionSet set, final List targets, - final Set targetIdsCancellList, final Map targetIdsToActions) { - - final List actions = targets.stream().map(target -> { + void sendTargetUpdatedEvents(final DistributionSet set, final List targets) { + targets.stream().forEach(target -> { target.setUpdateStatus(TargetUpdateStatus.PENDING); sendTargetUpdatedEvent(target); + }); + } + + @Override + DistributionSetAssignmentResult sendDistributionSetAssignedEvent(final DistributionSetAssignmentResult assignmentResult) { - return target; - }).filter(target -> !targetIdsCancellList.contains(target.getId())).map(Target::getControllerId) - .map(targetIdsToActions::get).collect(Collectors.toList()); + final List filtered = assignmentResult.getActions().stream().filter(action -> { + final Status actionStatus = action.getStatus(); + return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; + }).collect(Collectors.toList()); - sendTargetAssignDistributionSetEvent(set.getTenant(), set.getId(), actions); + final DistributionSet set = assignmentResult.getDistributionSet(); + sendTargetAssignDistributionSetEvent(set.getTenant(), set.getId(), filtered); + return assignmentResult; + } + + @Override + DistributionSetAssignmentResultMap sendDistributionSetsAssignedEvent( + final DistributionSetAssignmentResultMap assignmentResult) { + // TODO implement this method + return null; } @Override @@ -83,7 +97,8 @@ void closeActiveActions(final List> targetIds) { } @Override - void updateTargetStatus(final JpaDistributionSet set, final List> targetIds, final String currentUser) { + void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet set, final List> targetIds, + final String currentUser) { targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSetAndUpdateStatus(TargetUpdateStatus.PENDING, set, System.currentTimeMillis(), currentUser, tIds)); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java index 7cf48859e0..8c7a0fa568 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/ControllerManagementTest.java @@ -910,7 +910,7 @@ public void findMessagesByActionStatusId() { final DistributionSet testDs = testdataFactory.createDistributionSet("1"); final List testTarget = testdataFactory.createTargets(1); - final Long actionId = assignDistributionSet(testDs, testTarget).getActions().get(0); + final Long actionId = assignDistributionSet(testDs, testTarget).getActionIds().get(0); controllerManagement.addUpdateActionStatus(entityFactory.actionStatus().create(actionId) .status(Action.Status.RUNNING).messages(Lists.newArrayList("proceeding message 1"))); @@ -939,7 +939,7 @@ public void addActionStatusUpdatesUntilQuotaIsExceeded() { // test for informational status final Long actionId1 = assignDistributionSet(testdataFactory.createDistributionSet("ds1"), - testdataFactory.createTargets(1, "t1")).getActions().get(0); + testdataFactory.createTargets(1, "t1")).getActionIds().get(0); assertThat(actionId1).isNotNull(); for (int i = 0; i < maxStatusEntries; ++i) { controllerManagement.addInformationalActionStatus(entityFactory.actionStatus().create(actionId1) @@ -950,7 +950,7 @@ public void addActionStatusUpdatesUntilQuotaIsExceeded() { // test for update status (and mixed case) final Long actionId2 = assignDistributionSet(testdataFactory.createDistributionSet("ds2"), - testdataFactory.createTargets(1, "t2")).getActions().get(0); + testdataFactory.createTargets(1, "t2")).getActionIds().get(0); assertThat(actionId2).isNotEqualTo(actionId1); for (int i = 0; i < maxStatusEntries; ++i) { controllerManagement.addUpdateActionStatus(entityFactory.actionStatus().create(actionId2) @@ -973,7 +973,7 @@ public void createActionStatusWithTooManyMessages() { final int maxMessages = quotaManagement.getMaxMessagesPerActionStatus(); final Long actionId = assignDistributionSet(testdataFactory.createDistributionSet("ds1"), - testdataFactory.createTargets(1)).getActions().get(0); + testdataFactory.createTargets(1)).getActionIds().get(0); assertThat(actionId).isNotNull(); final List messages = Lists.newArrayList(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java index 4eb5882898..3abbd7e30e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java @@ -145,7 +145,7 @@ public void findActionWithLazyDetails() { new ArrayList()); final List testTarget = testdataFactory.createTargets(1); // one action with one action status is generated - final Long actionId = assignDistributionSet(testDs, testTarget).getActions().get(0); + final Long actionId = assignDistributionSet(testDs, testTarget).getActionIds().get(0); final Action action = deploymentManagement.findActionWithDetails(actionId).get(); assertThat(action.getDistributionSet()).as("DistributionSet in action").isNotNull(); @@ -162,7 +162,7 @@ public void findActionByTargetId() { new ArrayList()); final List testTarget = testdataFactory.createTargets(1); // one action with one action status is generated - final Long actionId = assignDistributionSet(testDs, testTarget).getActions().get(0); + final Long actionId = assignDistributionSet(testDs, testTarget).getActionIds().get(0); // act final Slice actions = deploymentManagement.findActionsByTarget(testTarget.get(0).getControllerId(), @@ -211,7 +211,7 @@ public void findActionStatusByActionId() { new ArrayList()); final List testTarget = testdataFactory.createTargets(1); // one action with one action status is generated - final Long actionId = assignDistributionSet(testDs, testTarget).getActions().get(0); + final Long actionId = assignDistributionSet(testDs, testTarget).getActionIds().get(0); final Slice actions = deploymentManagement.findActionsByTarget(testTarget.get(0).getControllerId(), PAGE); final ActionStatus expectedActionStatus = ((JpaAction) actions.getContent().get(0)).getActionStatus().get(0); @@ -230,7 +230,7 @@ public void findMessagesByActionStatusId() { new ArrayList()); final List testTarget = testdataFactory.createTargets(1); // one action with one action status is generated - final Long actionId = assignDistributionSet(testDs, testTarget).getActions().get(0); + final Long actionId = assignDistributionSet(testDs, testTarget).getActionIds().get(0); // create action-status entry with one message controllerManagement.addUpdateActionStatus(entityFactory.actionStatus().create(actionId) .status(Action.Status.FINISHED).messages(Arrays.asList("finished message"))); @@ -1021,7 +1021,7 @@ public void forceSoftAction() { ds.getId(), ActionType.SOFT, org.eclipse.hawkbit.repository.model.RepositoryModelConstants.NO_FORCE_TIME, Arrays.asList(target.getControllerId())); - final Long actionId = assignDistributionSet.getActions().get(0); + final Long actionId = assignDistributionSet.getActionIds().get(0); // verify preparation Action findAction = deploymentManagement.findAction(actionId).get(); assertThat(findAction.getActionType()).as("action type is wrong").isEqualTo(ActionType.SOFT); @@ -1045,7 +1045,7 @@ public void forceAlreadyForcedActionNothingChanges() { ds.getId(), ActionType.FORCED, org.eclipse.hawkbit.repository.model.RepositoryModelConstants.NO_FORCE_TIME, Arrays.asList(target.getControllerId())); - final Long actionId = assignDistributionSet.getActions().get(0); + final Long actionId = assignDistributionSet.getActionIds().get(0); // verify perparation Action findAction = deploymentManagement.findAction(actionId).get(); assertThat(findAction.getActionType()).as("action type is wrong").isEqualTo(ActionType.FORCED); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java index 1e91f5051b..2ea74db329 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutManagementTest.java @@ -104,7 +104,7 @@ public void rolloutShouldNotCancelRunningActionWithTheSameDistributionSet() { testdataFactory.createTarget(knownControllerId); final DistributionSetAssignmentResult assignmentResult = deploymentManagement.assignDistributionSet( knownDistributionSet.getId(), ActionType.FORCED, 0, Collections.singleton(knownControllerId)); - final Long manuallyAssignedActionId = assignmentResult.getActions().get(0); + final Long manuallyAssignedActionId = assignmentResult.getActionIds().get(0); // create rollout with the same distribution set already assigned // start rollout @@ -144,7 +144,7 @@ public void rolloutAssignesNewDistributionSetAndAutoCloseActiveActions() { testdataFactory.createTarget(knownControllerId); final DistributionSetAssignmentResult assignmentResult = deploymentManagement.assignDistributionSet( firstDistributionSet.getId(), ActionType.FORCED, 0, Collections.singleton(knownControllerId)); - final Long manuallyAssignedActionId = assignmentResult.getActions().get(0); + final Long manuallyAssignedActionId = assignmentResult.getActionIds().get(0); // create rollout with the same distribution set already assigned // start rollout diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java index df2c30242f..4a26268e4e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementSearchTest.java @@ -96,7 +96,7 @@ public void targetSearchWithVariousFilterCombinations() { final String assignedB = targBs.iterator().next().getControllerId(); assignDistributionSet(setA.getId(), assignedB); final String installedC = targCs.iterator().next().getControllerId(); - final Long actionId = assignDistributionSet(installedSet.getId(), assignedC).getActions().get(0); + final Long actionId = assignDistributionSet(installedSet.getId(), assignedC).getActionIds().get(0); // add attributes to match against only attribute value or attribute // value and name @@ -701,7 +701,7 @@ public void findTargetByInstalledDistributionSet() { List installedtargets = testdataFactory.createTargets(10, "assigned", "assigned"); // set on installed and assign another one - assignDistributionSet(installedSet, installedtargets).getActions().forEach(actionId -> controllerManagement + assignDistributionSet(installedSet, installedtargets).getActionIds().forEach(actionId -> controllerManagement .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Status.FINISHED))); assignDistributionSet(assignedSet, installedtargets); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java index e15e62a078..0ef1390f93 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/TargetManagementTest.java @@ -477,7 +477,7 @@ public void findTargetByControllerIDWithDetails() { final DistributionSetAssignmentResult result = assignDistributionSet(set.getId(), "4711"); controllerManagement.addUpdateActionStatus( - entityFactory.actionStatus().create(result.getActions().get(0)).status(Status.FINISHED)); + entityFactory.actionStatus().create(result.getActionIds().get(0)).status(Status.FINISHED)); assignDistributionSet(set2.getId(), "4711"); target = targetManagement.getByControllerID("4711").get(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java index bfa9885ec1..6500de2d62 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autoassign/AutoAssignCheckerTest.java @@ -59,7 +59,7 @@ public void autoAssignDistributionSetAndAutoCloseOldActions() { testdataFactory.createTarget(knownControllerId); final DistributionSetAssignmentResult assignmentResult = deploymentManagement.assignDistributionSet( firstDistributionSet.getId(), ActionType.FORCED, 0, Collections.singleton(knownControllerId)); - final Long manuallyAssignedActionId = assignmentResult.getActions().get(0); + final Long manuallyAssignedActionId = assignmentResult.getActionIds().get(0); // target filter query that matches all targets final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoActionCleanupTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoActionCleanupTest.java index 5d83ec12dd..89c1497b06 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoActionCleanupTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/autocleanup/AutoActionCleanupTest.java @@ -81,7 +81,7 @@ public void cleanupDisabled() { final DistributionSet ds2 = testdataFactory.createDistributionSet("ds2"); final Long action1 = deploymentManagement.assignDistributionSet(ds1.getId(), ActionType.FORCED, 0, - Collections.singletonList(trg1.getControllerId())).getActions().get(0); + Collections.singletonList(trg1.getControllerId())).getActionIds().get(0); deploymentManagement.assignDistributionSet(ds2.getId(), ActionType.FORCED, 0, Collections.singletonList(trg2.getControllerId())); @@ -110,11 +110,11 @@ public void canceledAndFailedActionsAreCleanedUp() { final DistributionSet ds2 = testdataFactory.createDistributionSet("ds2"); final Long action1 = deploymentManagement.assignDistributionSet(ds1.getId(), ActionType.FORCED, 0, - Collections.singletonList(trg1.getControllerId())).getActions().get(0); + Collections.singletonList(trg1.getControllerId())).getActionIds().get(0); final Long action2 = deploymentManagement.assignDistributionSet(ds2.getId(), ActionType.FORCED, 0, - Collections.singletonList(trg2.getControllerId())).getActions().get(0); + Collections.singletonList(trg2.getControllerId())).getActionIds().get(0); final Long action3 = deploymentManagement.assignDistributionSet(ds2.getId(), ActionType.FORCED, 0, - Collections.singletonList(trg3.getControllerId())).getActions().get(0); + Collections.singletonList(trg3.getControllerId())).getActionIds().get(0); assertThat(actionRepository.count()).isEqualTo(3); @@ -145,11 +145,11 @@ public void canceledActionsAreCleanedUp() { final DistributionSet ds2 = testdataFactory.createDistributionSet("ds2"); final Long action1 = deploymentManagement.assignDistributionSet(ds1.getId(), ActionType.FORCED, 0, - Collections.singletonList(trg1.getControllerId())).getActions().get(0); + Collections.singletonList(trg1.getControllerId())).getActionIds().get(0); final Long action2 = deploymentManagement.assignDistributionSet(ds2.getId(), ActionType.FORCED, 0, - Collections.singletonList(trg2.getControllerId())).getActions().get(0); + Collections.singletonList(trg2.getControllerId())).getActionIds().get(0); final Long action3 = deploymentManagement.assignDistributionSet(ds2.getId(), ActionType.FORCED, 0, - Collections.singletonList(trg3.getControllerId())).getActions().get(0); + Collections.singletonList(trg3.getControllerId())).getActionIds().get(0); assertThat(actionRepository.count()).isEqualTo(3); @@ -182,11 +182,11 @@ public void canceledAndFailedActionsAreCleanedUpWhenExpired() throws Interrupted final DistributionSet ds2 = testdataFactory.createDistributionSet("ds2"); final Long action1 = deploymentManagement.assignDistributionSet(ds1.getId(), ActionType.FORCED, 0, - Collections.singletonList(trg1.getControllerId())).getActions().get(0); + Collections.singletonList(trg1.getControllerId())).getActionIds().get(0); final Long action2 = deploymentManagement.assignDistributionSet(ds2.getId(), ActionType.FORCED, 0, - Collections.singletonList(trg2.getControllerId())).getActions().get(0); + Collections.singletonList(trg2.getControllerId())).getActionIds().get(0); final Long action3 = deploymentManagement.assignDistributionSet(ds2.getId(), ActionType.FORCED, 0, - Collections.singletonList(trg3.getControllerId())).getActions().get(0); + Collections.singletonList(trg3.getControllerId())).getActionIds().get(0); assertThat(actionRepository.count()).isEqualTo(3); diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiCancelActionTest.java b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiCancelActionTest.java index 495abe5608..b00769123d 100644 --- a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiCancelActionTest.java +++ b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiCancelActionTest.java @@ -35,7 +35,6 @@ import org.springframework.hateoas.MediaTypes; import org.springframework.http.MediaType; import org.springframework.integration.json.JsonPathUtils; -import org.springframework.test.util.JsonPathExpectationsHelper; import io.qameta.allure.Description; import io.qameta.allure.Feature; @@ -52,13 +51,13 @@ public class DdiCancelActionTest extends AbstractDDiApiIntegrationTest { @Description("Tests that the cancel action resource can be used with CBOR.") public void cancelActionCbor() throws Exception { final DistributionSet ds = testdataFactory.createDistributionSet(""); - final Target savedTarget = testdataFactory.createTarget(); - final Long actionId = assignDistributionSet(ds.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActions() + testdataFactory.createTarget(); + final Long actionId = assignDistributionSet(ds.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActionIds() .get(0); final Action cancelAction = deploymentManagement.cancelAction(actionId); // check that we can get the cancel action as CBOR - byte[] result = mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/" + final byte[] result = mvc.perform(get("/{tenant}/controller/v1/" + TestdataFactory.DEFAULT_CONTROLLER_ID + "/cancelAction/" + cancelAction.getId(), tenantAware.getCurrentTenant()).accept(DdiRestConstants.MEDIA_TYPE_CBOR)) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andExpect(content().contentType(DdiRestConstants.MEDIA_TYPE_CBOR_UTF8)) @@ -81,7 +80,7 @@ public void rootRsCancelActionButContinueAnyway() throws Exception { final DistributionSet ds = testdataFactory.createDistributionSet(""); final Target savedTarget = testdataFactory.createTarget(); - final Long actionId = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getActions().get(0); + final Long actionId = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getActionIds().get(0); final Action cancelAction = deploymentManagement.cancelAction(actionId); @@ -134,7 +133,7 @@ public void rootRsCancelAction() throws Exception { final DistributionSet ds = testdataFactory.createDistributionSet(""); final Target savedTarget = testdataFactory.createTarget(); - final Long actionId = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getActions().get(0); + final Long actionId = assignDistributionSet(ds.getId(), savedTarget.getControllerId()).getActionIds().get(0); long current = System.currentTimeMillis(); mvc.perform(get("/{tenant}/controller/v1/{controller}", tenantAware.getCurrentTenant(), @@ -250,7 +249,7 @@ private Action createCancelAction(final String targetid) { final Target savedTarget = testdataFactory.createTarget(targetid); final List toAssign = new ArrayList<>(); toAssign.add(savedTarget); - final Long actionId = assignDistributionSet(ds, toAssign).getActions().get(0); + final Long actionId = assignDistributionSet(ds, toAssign).getActionIds().get(0); return deploymentManagement.cancelAction(actionId); } @@ -263,7 +262,7 @@ public void rootRsCancelActionFeedback() throws Exception { final Target savedTarget = testdataFactory.createTarget(); - final Long actionId = assignDistributionSet(ds.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActions() + final Long actionId = assignDistributionSet(ds.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActionIds() .get(0); // cancel action manually @@ -338,11 +337,11 @@ public void multipleCancelActionFeedback() throws Exception { final Target savedTarget = testdataFactory.createTarget(); - final Long actionId = assignDistributionSet(ds.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActions() + final Long actionId = assignDistributionSet(ds.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActionIds() .get(0); - final Long actionId2 = assignDistributionSet(ds2.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActions() + final Long actionId2 = assignDistributionSet(ds2.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActionIds() .get(0); - final Long actionId3 = assignDistributionSet(ds3.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActions() + final Long actionId3 = assignDistributionSet(ds3.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActionIds() .get(0); assertThat(deploymentManagement.countActionStatusAll()).isEqualTo(3); @@ -452,7 +451,7 @@ public void tooMuchCancelActionFeedback() throws Exception { testdataFactory.createTarget(); final DistributionSet ds = testdataFactory.createDistributionSet(""); - final Long actionId = assignDistributionSet(ds.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActions() + final Long actionId = assignDistributionSet(ds.getId(), TestdataFactory.DEFAULT_CONTROLLER_ID).getActionIds() .get(0); final Action cancelAction = deploymentManagement.cancelAction(actionId); diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java index 5a342edc20..1fa4aade9d 100644 --- a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java +++ b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiDeploymentBaseTest.java @@ -267,7 +267,7 @@ public void changeEtagIfActionSwitchesFromSoftToForced() throws Exception { final DistributionSet ds = testdataFactory.createDistributionSet("", true); final Long actionId = deploymentManagement.assignDistributionSet(ds.getId(), ActionType.TIMEFORCED, - System.currentTimeMillis() + 2_000, Arrays.asList(target.getControllerId())).getActions().get(0); + System.currentTimeMillis() + 2_000, Arrays.asList(target.getControllerId())).getActionIds().get(0); MvcResult mvcResult = mvc.perform(get("/{tenant}/controller/v1/4712", tenantAware.getCurrentTenant())) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) @@ -566,7 +566,7 @@ public void badDeploymentAction() throws Exception { final List toAssign = Arrays.asList(target); final DistributionSet savedSet = testdataFactory.createDistributionSet(""); - final Long actionId = assignDistributionSet(savedSet, toAssign).getActions().get(0); + final Long actionId = assignDistributionSet(savedSet, toAssign).getActionIds().get(0); mvc.perform(get("/{tenant}/controller/v1/4712/deploymentBase/" + actionId, tenantAware.getCurrentTenant())) .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); mvc.perform(get("/{tenant}/controller/v1/4712/deploymentBase/" + actionId, tenantAware.getCurrentTenant()) @@ -639,9 +639,9 @@ public void multipleDeplomentActionFeedback() throws Exception { final List toAssign = new ArrayList<>(); toAssign.add(savedTarget1); - final Long actionId1 = assignDistributionSet(ds1.getId(), "4712").getActions().get(0); - final Long actionId2 = assignDistributionSet(ds2.getId(), "4712").getActions().get(0); - final Long actionId3 = assignDistributionSet(ds3.getId(), "4712").getActions().get(0); + final Long actionId1 = assignDistributionSet(ds1.getId(), "4712").getActionIds().get(0); + final Long actionId2 = assignDistributionSet(ds2.getId(), "4712").getActionIds().get(0); + final Long actionId3 = assignDistributionSet(ds3.getId(), "4712").getActionIds().get(0); Target myT = targetManagement.getByControllerID("4712").get(); assertThat(myT.getUpdateStatus()).isEqualTo(TargetUpdateStatus.PENDING); diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java index 9e9159cbbf..63e2515f64 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTargetResourceTest.java @@ -1239,7 +1239,7 @@ public void updateAction() throws Exception { final DistributionSet set = testdataFactory.createDistributionSet(); final Long actionId = deploymentManagement .assignDistributionSet(set.getId(), ActionType.SOFT, 0, Arrays.asList(target.getControllerId())) - .getActions().get(0); + .getActionIds().get(0); assertThat(deploymentManagement.findAction(actionId).get().getActionType()).isEqualTo(ActionType.SOFT); final String body = new JSONObject().put("forceType", "forced").toString(); diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java index 4d2bb24fe5..c0907ff17c 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java +++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/ddi/documentation/RootControllerDocumentationTest.java @@ -138,7 +138,7 @@ public void getControllerCancelAction() throws Exception { final Target target = targetManagement.create(entityFactory.target().create().controllerId(CONTROLLER_ID)); final Long actionId = deploymentManagement - .assignDistributionSet(set.getId(), Arrays.asList(target.getTargetWithActionType())).getActions() + .assignDistributionSet(set.getId(), Arrays.asList(target.getTargetWithActionType())).getActionIds() .get(0); final Action cancelAction = deploymentManagement.cancelAction(actionId); @@ -171,7 +171,7 @@ public void postCancelActionFeedback() throws Exception { final Target target = targetManagement.create(entityFactory.target().create().controllerId(CONTROLLER_ID)); final Long actionId = deploymentManagement - .assignDistributionSet(set.getId(), Arrays.asList(target.getTargetWithActionType())).getActions() + .assignDistributionSet(set.getId(), Arrays.asList(target.getTargetWithActionType())).getActionIds() .get(0); final Action cancelAction = deploymentManagement.cancelAction(actionId); @@ -265,7 +265,7 @@ public void getControllerBasedeploymentAction() throws Exception { final Target target = targetManagement.create(entityFactory.target().create().controllerId(CONTROLLER_ID)); final Long actionId = assignDistributionSetWithMaintenanceWindow(set.getId(), target.getControllerId(), - getTestSchedule(-5), getTestDuration(10), getTestTimeZone()).getActions().get(0); + getTestSchedule(-5), getTestDuration(10), getTestTimeZone()).getActionIds().get(0); controllerManagement.addInformationalActionStatus( entityFactory.actionStatus().create(actionId).message("Started download").status(Status.DOWNLOAD)); @@ -347,7 +347,7 @@ public void getControllerBasedeploymentActionWithMaintenanceWindow() throws Exce final Target target = targetManagement.create(entityFactory.target().create().controllerId(CONTROLLER_ID)); final Long actionId = assignDistributionSetWithMaintenanceWindow(set.getId(), target.getControllerId(), - getTestSchedule(2), getTestDuration(1), getTestTimeZone()).getActions().get(0); + getTestSchedule(2), getTestDuration(1), getTestTimeZone()).getActionIds().get(0); mockMvc.perform(get( DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" + DdiRestConstants.DEPLOYMENT_BASE_ACTION @@ -388,7 +388,7 @@ public void postBasedeploymentActionFeedback() throws Exception { final Target target = targetManagement.create(entityFactory.target().create().controllerId(CONTROLLER_ID)); final Long actionId = deploymentManagement - .assignDistributionSet(set.getId(), Arrays.asList(target.getTargetWithActionType())).getActions() + .assignDistributionSet(set.getId(), Arrays.asList(target.getTargetWithActionType())).getActionIds() .get(0); mockMvc.perform(post(DdiRestConstants.BASE_V1_REQUEST_MAPPING + "/{controllerId}/" diff --git a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetResourceDocumentationTest.java b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetResourceDocumentationTest.java index 248b4093d2..ebc3e2b7e6 100644 --- a/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetResourceDocumentationTest.java +++ b/hawkbit-rest/hawkbit-rest-docs/src/test/java/org/eclipse/hawkbit/rest/mgmt/documentation/TargetResourceDocumentationTest.java @@ -397,7 +397,7 @@ public void switchActionToForced() throws Exception { final DistributionSet set = testdataFactory.createDistributionSet(); final Long actionId = deploymentManagement .assignDistributionSet(set.getId(), ActionType.SOFT, 0, Arrays.asList(target.getControllerId())) - .getActions().get(0); + .getActionIds().get(0); assertThat(deploymentManagement.findAction(actionId).get().getActionType()).isEqualTo(ActionType.SOFT); final Map body = new HashMap<>(); From b73e452c1f0b52eb66ed935056fad7c8c2257d03 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Wed, 3 Apr 2019 10:23:41 +0200 Subject: [PATCH 12/56] Added new DMF message DmfMultiActionRequest Signed-off-by: Stefan Behl --- .../dmf/json/model/DmfMultiActionRequest.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java diff --git a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java new file mode 100644 index 0000000000..c1f6e81453 --- /dev/null +++ b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2019 Bosch Software Innovations 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 java.util.Arrays; +import java.util.List; + +import org.eclipse.hawkbit.dmf.amqp.api.EventTopic; + +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 com.fasterxml.jackson.annotation.JsonValue; + +/** + * JSON representation of a multi-action request. + */ +@JsonInclude(Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class DmfMultiActionRequest { + + private List elements; + + @JsonValue + public List getElements() { + return elements; + } + + public void addElement(final DmfMultiActionElement element) { + if (elements != null) { + elements.add(element); + } else { + elements = Arrays.asList(element); + } + } + + public static class DmfMultiActionElement { + + @JsonProperty + private EventTopic actionType; + + @JsonProperty + private DmfDownloadAndUpdateRequest action; + + public DmfDownloadAndUpdateRequest getAction() { + return action; + } + + public void setAction(final DmfDownloadAndUpdateRequest action) { + this.action = action; + } + + public EventTopic getActionType() { + return actionType; + } + + public void setActionType(final EventTopic actionType) { + this.actionType = actionType; + } + + } + +} From 19d48c2dc48c1c67d49b22998b79a75d88746b32 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Wed, 3 Apr 2019 16:15:25 +0200 Subject: [PATCH 13/56] DMF enhancements to send out MultiActionRequest messages Signed-off-by: Stefan Behl --- .../hawkbit/amqp/AmqpConfiguration.java | 11 +- .../amqp/AmqpMessageDispatcherService.java | 129 +++++++++++++++++- .../amqp/AmqpMessageHandlerService.java | 39 +++++- .../AmqpControllerAuthenticationTest.java | 16 +-- .../AmqpMessageDispatcherServiceTest.java | 5 +- .../amqp/AmqpMessageHandlerServiceTest.java | 12 +- .../hawkbit/dmf/amqp/api/EventTopic.java | 12 +- .../dmf/json/model/DmfMultiActionRequest.java | 13 +- .../repository/DeploymentManagement.java | 5 +- .../event/remote/DeploymentEvent.java | 57 ++++++++ .../jpa/AbstractDsAssignmentStrategy.java | 35 +---- .../jpa/JpaDeploymentManagement.java | 26 ++-- .../jpa/OfflineDsAssignmentStrategy.java | 27 ++-- .../jpa/OnlineDsAssignmentStrategy.java | 72 +++++++--- 14 files changed, 357 insertions(+), 102 deletions(-) create mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java index 7b0674c88c..2ae1066280 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpConfiguration.java @@ -17,6 +17,7 @@ import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings; import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.ControllerManagement; +import org.eclipse.hawkbit.repository.DeploymentManagement; import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.SoftwareModuleManagement; @@ -240,9 +241,11 @@ public Binding bindDeadLetterQueueToDeadLetterExchange() { @Bean public AmqpMessageHandlerService amqpMessageHandlerService(final RabbitTemplate rabbitTemplate, final AmqpMessageDispatcherService amqpMessageDispatcherService, - final ControllerManagement controllerManagement, final EntityFactory entityFactory) { + final ControllerManagement controllerManagement, final EntityFactory entityFactory, + final SystemSecurityContext systemSecurityContext, + final TenantConfigurationManagement tenantConfigurationManagement) { return new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherService, controllerManagement, - entityFactory); + entityFactory, systemSecurityContext, tenantConfigurationManagement); } /** @@ -313,10 +316,10 @@ AmqpMessageDispatcherService amqpMessageDispatcherService(final RabbitTemplate r final AmqpMessageSenderService amqpSenderService, final ArtifactUrlHandler artifactUrlHandler, final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement, final TargetManagement targetManagement, final DistributionSetManagement distributionSetManagement, - final SoftwareModuleManagement softwareModuleManagement) { + final SoftwareModuleManagement softwareModuleManagement, final DeploymentManagement deploymentManagement) { return new AmqpMessageDispatcherService(rabbitTemplate, amqpSenderService, artifactUrlHandler, systemSecurityContext, systemManagement, targetManagement, serviceMatcher, distributionSetManagement, - softwareModuleManagement); + softwareModuleManagement, deploymentManagement); } private static Map getTTLMaxArgsAuthenticationQueue() { diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java index 080c293031..791b02012b 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java @@ -10,6 +10,7 @@ import java.net.URI; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -28,17 +29,22 @@ import org.eclipse.hawkbit.dmf.json.model.DmfArtifactHash; 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.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.event.remote.DeploymentEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent; +import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Artifact; +import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.Target; @@ -70,6 +76,8 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { private static final Logger LOG = LoggerFactory.getLogger(AmqpMessageDispatcherService.class); + private static final int MAX_ACTIONS = 100; + private final ArtifactUrlHandler artifactUrlHandler; private final AmqpMessageSenderService amqpSenderService; private final SystemSecurityContext systemSecurityContext; @@ -77,6 +85,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { private final TargetManagement targetManagement; private final ServiceMatcher serviceMatcher; private final DistributionSetManagement distributionSetManagement; + private final DeploymentManagement deploymentManagement; private final SoftwareModuleManagement softwareModuleManagement; /** @@ -105,7 +114,7 @@ protected AmqpMessageDispatcherService(final RabbitTemplate rabbitTemplate, final SystemSecurityContext systemSecurityContext, final SystemManagement systemManagement, final TargetManagement targetManagement, final ServiceMatcher serviceMatcher, final DistributionSetManagement distributionSetManagement, - final SoftwareModuleManagement softwareModuleManagement) { + final SoftwareModuleManagement softwareModuleManagement, final DeploymentManagement deploymentManagement) { super(rabbitTemplate); this.artifactUrlHandler = artifactUrlHandler; this.amqpSenderService = amqpSenderService; @@ -115,6 +124,7 @@ protected AmqpMessageDispatcherService(final RabbitTemplate rabbitTemplate, this.serviceMatcher = serviceMatcher; this.distributionSetManagement = distributionSetManagement; this.softwareModuleManagement = softwareModuleManagement; + this.deploymentManagement = deploymentManagement; } /** @@ -151,6 +161,91 @@ protected void targetAssignDistributionSet(final TargetAssignDistributionSetEven }); } + /** + * Method to send a message to a RabbitMQ exchange after the Distribution + * set has been assign to a Target. + * + * @param e + * the object to be send. + */ + @EventListener(classes = DeploymentEvent.class) + protected void onDeployment(final DeploymentEvent e) { + if (isNotFromSelf(e)) { + return; + } + sendMultiActionRequestMessages(e.getTenant(), e.getControllerIds()); + } + + protected void sendMultiActionRequestMessages(final String tenant, final List controllerIds) { + + final Map>> softwareModuleMetadata = new HashMap<>(); + targetManagement.getByControllerID(controllerIds).stream().forEach(target -> { + + final List activeActions = deploymentManagement + .findActiveActionsByTarget(PageRequest.of(0, MAX_ACTIONS), target.getControllerId()).getContent(); + + activeActions.forEach(action -> { + final DistributionSet distributionSet = action.getDistributionSet(); + softwareModuleMetadata.computeIfAbsent(distributionSet.getId(), + id -> getSoftwareModuleMetadata(distributionSet)); + }); + + if (!activeActions.isEmpty()) { + sendMultiActionRequestToTarget(tenant, target, activeActions, softwareModuleMetadata); + } + + }); + + } + + protected void sendMultiActionRequestToTarget(final String tenant, final Target target, final List actions, + final Map>> softwareModulesPerDistributionSet) { + + final URI targetAdress = target.getAddress(); + if (!IpUtil.isAmqpUri(targetAdress)) { + return; + } + + final DmfMultiActionRequest multiActionRequest = new DmfMultiActionRequest(); + actions.forEach(action -> { + final DmfActionRequest actionRequest = createDmfActionRequest(target, action, + softwareModulesPerDistributionSet.get(action.getDistributionSet().getId())); + multiActionRequest.addElement(getEventTypeForAction(action), actionRequest); + }); + + final Message message = getMessageConverter().toMessage(multiActionRequest, + createConnectorMessagePropertiesEvent(tenant, target.getControllerId(), EventTopic.MULTI_ACTION)); + amqpSenderService.sendMessage(message, targetAdress); + + } + + private DmfActionRequest createDmfActionRequest(final Target target, final Action action, + final Map> softwareModules) { + if (action.isCancelingOrCanceled()) { + return createPlainActionRequest(action); + } + return createDownloadAndUpdateRequest(target, action, softwareModules); + + } + + private DmfActionRequest createPlainActionRequest(final Action action) { + final DmfActionRequest actionRequest = new DmfActionRequest(); + actionRequest.setActionId(action.getId()); + return actionRequest; + } + + private DmfDownloadAndUpdateRequest createDownloadAndUpdateRequest(final Target target, final Action action, + final Map> softwareModules) { + final DmfDownloadAndUpdateRequest request = new DmfDownloadAndUpdateRequest(); + request.setActionId(action.getId()); + request.setTargetSecurityToken(systemSecurityContext.runAsSystem(target::getSecurityToken)); + if (softwareModules != null) { + softwareModules.entrySet() + .forEach(entry -> request.addSoftwareModule(convertToAmqpSoftwareModule(target, entry))); + } + return request; + } + /** * Method to get the type of event depending on whether the action has a * valid maintenance window available or not based on defined maintenance @@ -167,6 +262,25 @@ private static EventTopic getEventTypeForTarget(final boolean maintenanceWindowA return maintenanceWindowAvailable ? EventTopic.DOWNLOAD_AND_INSTALL : EventTopic.DOWNLOAD; } + /** + * Method to get the type of event depending on whether the action has a + * valid maintenance window available or not based on defined maintenance + * schedule. In case of no maintenance schedule or if there is a valid + * window available, the topic {@link EventTopic#DOWNLOAD_AND_INSTALL} is + * returned else {@link EventTopic#DOWNLOAD} is returned. + * + * @param maintenanceWindowAvailable + * valid maintenance window or not. + * + * @return {@link EventTopic} to use for message. + */ + private static EventTopic getEventTypeForAction(final Action action) { + if (action.isCancelingOrCanceled()) { + return EventTopic.CANCEL_DOWNLOAD; + } + return getEventTypeForTarget(action.isMaintenanceWindowAvailable()); + } + /** * Method to send a message to a RabbitMQ Exchange after the assignment of * the Distribution set to a Target has been canceled. @@ -358,4 +472,17 @@ private DmfArtifact convertArtifact(final Target target, final Artifact localArt return artifact; } + private Map> getSoftwareModuleMetadata( + final DistributionSet distributionSet) { + final Map> moduleMetadata = Maps + .newHashMapWithExpectedSize(distributionSet.getModules().size()); + distributionSet.getModules() + .forEach( + module -> moduleMetadata.put(module, + softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible( + PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT), module.getId()) + .getContent())); + return moduleMetadata; + } + } diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java index 1cc6752c9c..e8b16fce12 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java @@ -8,6 +8,9 @@ */ package org.eclipse.hawkbit.amqp; +import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED; + +import java.io.Serializable; import java.net.URI; import java.util.Collections; import java.util.List; @@ -27,6 +30,7 @@ import org.eclipse.hawkbit.repository.ControllerManagement; import org.eclipse.hawkbit.repository.EntityFactory; import org.eclipse.hawkbit.repository.RepositoryConstants; +import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.UpdateMode; import org.eclipse.hawkbit.repository.builder.ActionStatusCreate; import org.eclipse.hawkbit.repository.model.Action; @@ -34,6 +38,7 @@ import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.Target; +import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.util.IpUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,6 +74,10 @@ public class AmqpMessageHandlerService extends BaseAmqpService { private final EntityFactory entityFactory; + private final TenantConfigurationManagement tenantConfigurationManagement; + + private final SystemSecurityContext systemSecurityContext; + /** * Constructor. * @@ -83,11 +92,15 @@ public class AmqpMessageHandlerService extends BaseAmqpService { */ public AmqpMessageHandlerService(final RabbitTemplate rabbitTemplate, final AmqpMessageDispatcherService amqpMessageDispatcherService, - final ControllerManagement controllerManagement, final EntityFactory entityFactory) { + final ControllerManagement controllerManagement, final EntityFactory entityFactory, + final SystemSecurityContext systemSecurityContext, + final TenantConfigurationManagement tenantConfigurationManagement) { super(rabbitTemplate); this.amqpMessageDispatcherService = amqpMessageDispatcherService; this.controllerManagement = controllerManagement; this.entityFactory = entityFactory; + this.systemSecurityContext = systemSecurityContext; + this.tenantConfigurationManagement = tenantConfigurationManagement; } /** @@ -190,11 +203,19 @@ private void registerTarget(final Message message, final String virtualHost) { final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(thingId, amqpUri); LOG.debug("Target {} reported online state.", thingId); - lookIfUpdateAvailable(target); + checkIfUpdatesAvailable(target); } - private void lookIfUpdateAvailable(final Target target) { + private void checkIfUpdatesAvailable(final Target target) { + if (isMultiAssignmentsEnabled()) { + amqpMessageDispatcherService.sendMultiActionRequestMessages(target.getTenant(), + Collections.singletonList(target.getControllerId())); + } else { + checkIfUpdateAvailable(target); + } + } + private void checkIfUpdateAvailable(final Target target) { final Optional actionOptional = controllerManagement .findOldestActiveActionByTarget(target.getControllerId()); @@ -278,7 +299,7 @@ private void updateActionStatus(final Message message) { if (!addUpdateActionStatus.isActive() || (addUpdateActionStatus.hasMaintenanceSchedule() && addUpdateActionStatus.isMaintenanceWindowAvailable())) { - lookIfUpdateAvailable(action.getTarget()); + checkIfUpdatesAvailable(action.getTarget()); } } @@ -372,4 +393,14 @@ private static UpdateMode getUpdateMode(final DmfAttributeUpdate update) { return null; } + private boolean isMultiAssignmentsEnabled() { + return getConfigValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class, Boolean.TRUE); + } + + private T getConfigValue(final String key, final Class valueType, + final T defaultValue) { + return systemSecurityContext + .runAsSystem(() -> tenantConfigurationManagement.getConfigurationValue(key, valueType, defaultValue)); + } + } diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java index 5b36ceff94..f4054d375d 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java @@ -22,7 +22,6 @@ 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; @@ -109,7 +108,7 @@ public class AmqpControllerAuthenticationTest { private ControllerManagement controllerManagementMock; @Mock - private Target targteMock; + private Target targetMock; private static final TenantConfigurationValue CONFIG_VALUE_FALSE = TenantConfigurationValue . builder().value(Boolean.FALSE).build(); @@ -137,11 +136,11 @@ public void before() throws Exception { .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 +161,12 @@ public void before() throws Exception { 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 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, diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java index e292210e49..72381c4b14 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherServiceTest.java @@ -13,7 +13,6 @@ 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 void before() throws Exception { 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 void before() throws Exception { amqpMessageDispatcherService = new AmqpMessageDispatcherService(rabbitTemplate, senderService, artifactUrlHandlerMock, systemSecurityContext, systemManagement, targetManagement, serviceMatcher, - distributionSetManagement, softwareModuleManagement); + distributionSetManagement, softwareModuleManagement, deploymentManagement); } diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java index a289756cf1..32fcd0fd57 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java @@ -40,6 +40,7 @@ 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; @@ -51,7 +52,9 @@ import org.eclipse.hawkbit.repository.model.Target; 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 +103,9 @@ public class AmqpMessageHandlerServiceTest { @Mock private ArtifactManagement artifactManagementMock; + @Mock + private TenantConfigurationManagement tenantConfigurationManagement; + @Mock private AmqpControllerAuthentication authenticationManagerMock; @@ -133,11 +139,11 @@ public void before() throws Exception { when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter); when(artifactManagementMock.findFirstBySHA1(SHA1)).thenReturn(Optional.empty()); - amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherServiceMock, - controllerManagementMock, entityFactoryMock); + final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(); + final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware); amqpMessageHandlerService = new AmqpMessageHandlerService(rabbitTemplate, amqpMessageDispatcherServiceMock, - controllerManagementMock, entityFactoryMock); + controllerManagementMock, entityFactoryMock, systemSecurityContext, tenantConfigurationManagement); amqpAuthenticationMessageHandlerService = new AmqpAuthenticationMessageHandler(rabbitTemplate, authenticationManagerMock, artifactManagementMock, downloadIdCache, hostnameResolverMock, controllerManagementMock, tenantAwareMock); diff --git a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/amqp/api/EventTopic.java b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/amqp/api/EventTopic.java index 29bb5bd752..123f564ae2 100644 --- a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/amqp/api/EventTopic.java +++ b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/amqp/api/EventTopic.java @@ -17,25 +17,35 @@ public enum EventTopic { * Topic when sending and receiving a update status. */ UPDATE_ACTION_STATUS, + /** * Topic when sending and receiving a download and install task. */ DOWNLOAD_AND_INSTALL, + /** * Topic when sending and receiving a cancel download task. */ CANCEL_DOWNLOAD, + /** * Topic when updating device attributes. */ UPDATE_ATTRIBUTES, + /** * Topic when sending a download only task, skipping the install. */ DOWNLOAD, + /** * Topic when an update of device attributes is requested. */ - REQUEST_ATTRIBUTES_UPDATE; + REQUEST_ATTRIBUTES_UPDATE, + + /** + * Topic to send multiple actions to the device. + */ + MULTI_ACTION; } diff --git a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java index c1f6e81453..7e061e84e5 100644 --- a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java +++ b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java @@ -41,19 +41,26 @@ public void addElement(final DmfMultiActionElement element) { } } + public void addElement(final EventTopic actionType, final DmfActionRequest action) { + final DmfMultiActionElement element = new DmfMultiActionElement(); + element.setActionType(actionType); + element.setAction(action); + addElement(element); + } + public static class DmfMultiActionElement { @JsonProperty private EventTopic actionType; @JsonProperty - private DmfDownloadAndUpdateRequest action; + private DmfActionRequest action; - public DmfDownloadAndUpdateRequest getAction() { + public DmfActionRequest getAction() { return action; } - public void setAction(final DmfDownloadAndUpdateRequest action) { + public void setAction(final DmfActionRequest action) { this.action = action; } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java index ace9614503..4e76e1b15d 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/DeploymentManagement.java @@ -30,7 +30,6 @@ import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; -import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResultMap; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.Target; @@ -114,7 +113,7 @@ DistributionSetAssignmentResult assignDistributionSet(long dsID, * the set of IDs of the distribution sets to assign * @param targets * a list of all targets and their action type - * @return the assignment result map + * @return the list of assignment results * * @throws IncompleteDistributionSetException * if mandatory {@link SoftwareModuleType} are not assigned as @@ -129,7 +128,7 @@ DistributionSetAssignmentResult assignDistributionSet(long dsID, * assigned to at once is exceeded */ @PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET) - DistributionSetAssignmentResultMap assignDistributionSets(@NotEmpty Set dsIDs, + List assignDistributionSets(@NotEmpty Set dsIDs, @NotEmpty Collection targets); /** diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java new file mode 100644 index 0000000000..f88612c8a4 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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.repository.event.remote; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +/** + * TenantAwareEvent that gets sent when a distribution set gets assigned to a + * target. + */ +public class DeploymentEvent extends RemoteTenantAwareEvent implements Iterable { + + private static final long serialVersionUID = 1L; + + private final List controllerIds = Arrays.asList(); + + /** + * Default constructor. + */ + public DeploymentEvent() { + // for serialization libs like jackson + } + + /** + * Constructor. + * + * @param tenant + * of the event + * @param applicationId + * the application id. + * @param controllerIds + * the controller IDs + */ + public DeploymentEvent(final String tenant, final String applicationId, + final List controllerIds) { + super(applicationId, tenant, applicationId); + this.controllerIds.addAll(controllerIds); + } + + public List getControllerIds() { + return controllerIds; + } + + @Override + public Iterator iterator() { + return controllerIds.iterator(); + } + +} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java index 0d7de08370..2f798c80cb 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java @@ -17,7 +17,6 @@ import org.eclipse.hawkbit.repository.QuotaManagement; import org.eclipse.hawkbit.repository.RepositoryConstants; -import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent; import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent; import org.eclipse.hawkbit.repository.jpa.configuration.Constants; @@ -31,14 +30,12 @@ import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; -import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResultMap; import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.bus.BusProperties; import org.springframework.context.ApplicationEventPublisher; -import org.springframework.util.CollectionUtils; /** * {@link DistributionSet} to {@link Target} assignment strategy as utility for @@ -125,35 +122,11 @@ abstract void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet */ abstract void closeActiveActions(List> targetIds); - /** - * Sends the assignment events for the given - * {@link DistributionSetAssignmentResult}. - * - * @param assignmentResult - * the assignment result - */ - abstract DistributionSetAssignmentResult sendDistributionSetAssignedEvent( - final DistributionSetAssignmentResult assignmentResult); - - /** - * Sends the assignment events for the given - * {@link DistributionSetAssignmentResultMap}. - * - * @param assignmentResults - * the results of multiple assignments - */ - abstract DistributionSetAssignmentResultMap sendDistributionSetsAssignedEvent( - final DistributionSetAssignmentResultMap assignmentResult); + abstract void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult, + final boolean deviceCanProcessMultipleActions); - protected void sendTargetAssignDistributionSetEvent(final String tenant, final long distributionSetId, - final List actions) { - if (CollectionUtils.isEmpty(actions)) { - return; - } - - afterCommit.afterCommit(() -> eventPublisher.publishEvent(new TargetAssignDistributionSetEvent(tenant, - distributionSetId, actions, bus.getId(), actions.get(0).isMaintenanceWindowAvailable()))); - } + abstract void sendDeploymentEvents(final List assignmentResults, + final boolean deviceCanProcessMultipleActions); protected void sendTargetUpdatedEvent(final JpaTarget target) { afterCommit.afterCommit(() -> eventPublisher.publishEvent(new TargetUpdatedEvent(target, bus.getId()))); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index 3a8dc0e89a..d949c7d86a 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -62,7 +62,6 @@ import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; -import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResultMap; import org.eclipse.hawkbit.repository.model.DistributionSetType; import org.eclipse.hawkbit.repository.model.SoftwareModuleType; import org.eclipse.hawkbit.repository.model.Target; @@ -183,7 +182,8 @@ public DistributionSetAssignmentResult offlineAssignedDistributionSet(final Long .map(controllerId -> new TargetWithActionType(controllerId, ActionType.FORCED, -1)) .collect(Collectors.toList()), null, offlineDsAssignmentStrategy); - return offlineDsAssignmentStrategy.sendDistributionSetAssignedEvent(result); + offlineDsAssignmentStrategy.sendDeploymentEvents(result, isMultiAssignmentsEnabled()); + return result; } @Override @@ -198,7 +198,8 @@ public DistributionSetAssignmentResult assignDistributionSet(final long dsID, fi .map(controllerId -> new TargetWithActionType(controllerId, actionType, forcedTimestamp)) .collect(Collectors.toList()), null, onlineDsAssignmentStrategy); - return onlineDsAssignmentStrategy.sendDistributionSetAssignedEvent(result); + onlineDsAssignmentStrategy.sendDeploymentEvents(result, isMultiAssignmentsEnabled()); + return result; } @Override @@ -208,17 +209,21 @@ public DistributionSetAssignmentResult assignDistributionSet(final long dsID, fi public DistributionSetAssignmentResult assignDistributionSet(final long dsID, final Collection targets) { - return assignDistributionSetToTargets(dsID, targets, null, onlineDsAssignmentStrategy); + final DistributionSetAssignmentResult result = assignDistributionSetToTargets(dsID, targets, null, + onlineDsAssignmentStrategy); + onlineDsAssignmentStrategy.sendDeploymentEvents(result, isMultiAssignmentsEnabled()); + return result; } @Override - public DistributionSetAssignmentResultMap assignDistributionSets(final Set dsIDs, + public List assignDistributionSets(final Set dsIDs, final Collection targets) { - final DistributionSetAssignmentResultMap results = new DistributionSetAssignmentResultMap(); - dsIDs.forEach(dsID -> results.putResult(dsID, - assignDistributionSetToTargets(dsID, targets, null, onlineDsAssignmentStrategy))); - return onlineDsAssignmentStrategy.sendDistributionSetsAssignedEvent(results); + final List results = dsIDs.stream() + .map(dsID -> assignDistributionSetToTargets(dsID, targets, null, onlineDsAssignmentStrategy)) + .collect(Collectors.toList()); + onlineDsAssignmentStrategy.sendDeploymentEvents(results, isMultiAssignmentsEnabled()); + return results; } @Override @@ -230,7 +235,8 @@ public DistributionSetAssignmentResult assignDistributionSet(final long dsID, final DistributionSetAssignmentResult result = assignDistributionSetToTargets(dsID, targets, actionMessage, onlineDsAssignmentStrategy); - return onlineDsAssignmentStrategy.sendDistributionSetAssignedEvent(result); + onlineDsAssignmentStrategy.sendDeploymentEvents(result, isMultiAssignmentsEnabled()); + return result; } /** diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java index 5db95548c2..ac531087b9 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java @@ -28,7 +28,6 @@ import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; -import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResultMap; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.springframework.cloud.bus.BusProperties; @@ -59,20 +58,6 @@ void sendTargetUpdatedEvents(final DistributionSet set, final List ta }); } - @Override - DistributionSetAssignmentResult sendDistributionSetAssignedEvent( - final DistributionSetAssignmentResult assignmentResult) { - // no need to send an event in the offline case - return assignmentResult; - } - - @Override - DistributionSetAssignmentResultMap sendDistributionSetsAssignedEvent( - final DistributionSetAssignmentResultMap assignmentResults) { - // no need to send an event in the offline case - return assignmentResults; - } - @Override public List findTargetsForAssignment(final List controllerIDs, final long setId) { return Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream() @@ -118,4 +103,16 @@ protected JpaActionStatus createActionStatus(final JpaAction action, final Strin return result; } + @Override + void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult, + final boolean deviceCanProcessMultipleActions) { + // no need to send deployment events in the offline case + } + + @Override + void sendDeploymentEvents(final List assignmentResults, + final boolean deviceCanProcessMultipleActions) { + // no need to send deployment events in the offline case + } + } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java index a28d778d20..615e7f1f1b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java @@ -9,12 +9,15 @@ package org.eclipse.hawkbit.repository.jpa; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; import org.eclipse.hawkbit.repository.QuotaManagement; +import org.eclipse.hawkbit.repository.event.remote.DeploymentEvent; +import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; import org.eclipse.hawkbit.repository.jpa.model.JpaAction; @@ -26,11 +29,11 @@ import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; -import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResultMap; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.springframework.cloud.bus.BusProperties; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.util.CollectionUtils; import com.google.common.collect.Lists; @@ -57,24 +60,37 @@ void sendTargetUpdatedEvents(final DistributionSet set, final List ta } @Override - DistributionSetAssignmentResult sendDistributionSetAssignedEvent(final DistributionSetAssignmentResult assignmentResult) { - - final List filtered = assignmentResult.getActions().stream().filter(action -> { - final Status actionStatus = action.getStatus(); - return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; - }).collect(Collectors.toList()); - - final DistributionSet set = assignmentResult.getDistributionSet(); - sendTargetAssignDistributionSetEvent(set.getTenant(), set.getId(), filtered); - - return assignmentResult; + void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult, + final boolean deviceCanProcessMultipleActions) { + if (deviceCanProcessMultipleActions) { + sendDeploymentEvents(Collections.singletonList(assignmentResult), deviceCanProcessMultipleActions); + } else { + sendDistributionSetAssignedEvent(assignmentResult); + } } @Override - DistributionSetAssignmentResultMap sendDistributionSetsAssignedEvent( - final DistributionSetAssignmentResultMap assignmentResult) { - // TODO implement this method - return null; + void sendDeploymentEvents(final List assignmentResults, + final boolean deviceCanProcessMultipleActions) { + + final List actions = assignmentResults.stream().flatMap(result -> result.getActions().stream()) + .filter(action -> { + final Status actionStatus = action.getStatus(); + return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; + }).collect(Collectors.toList()); + + if (deviceCanProcessMultipleActions) { + final List controllerIds = actions.stream().map(action -> action.getTarget().getControllerId()) + .collect(Collectors.toList()); + if (!actions.isEmpty()) { + final String tenant = actions.get(0).getTenant(); + afterCommit.afterCommit( + () -> eventPublisher.publishEvent(new DeploymentEvent(tenant, bus.getId(), controllerIds))); + } + } else { + assignmentResults.forEach(this::sendDistributionSetAssignedEvent); + } + } @Override @@ -121,4 +137,28 @@ JpaActionStatus createActionStatus(final JpaAction action, final String actionMe return result; } + private DistributionSetAssignmentResult sendDistributionSetAssignedEvent( + final DistributionSetAssignmentResult assignmentResult) { + + final List filtered = assignmentResult.getActions().stream().filter(action -> { + final Status actionStatus = action.getStatus(); + return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; + }).collect(Collectors.toList()); + + final DistributionSet set = assignmentResult.getDistributionSet(); + sendTargetAssignDistributionSetEvent(set.getTenant(), set.getId(), filtered); + + return assignmentResult; + } + + private void sendTargetAssignDistributionSetEvent(final String tenant, final long distributionSetId, + final List actions) { + if (CollectionUtils.isEmpty(actions)) { + return; + } + + afterCommit.afterCommit(() -> eventPublisher.publishEvent(new TargetAssignDistributionSetEvent(tenant, + distributionSetId, actions, bus.getId(), actions.get(0).isMaintenanceWindowAvailable()))); + } + } From d35d6d544ac691c9ff134923e12d00f1d3ccc0f0 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Wed, 3 Apr 2019 17:16:55 +0200 Subject: [PATCH 14/56] Minor changes Signed-off-by: Stefan Behl --- .../amqp/AmqpMessageDispatcherService.java | 4 +++- .../jpa/JpaDeploymentManagement.java | 4 ++-- .../management/targettable/TargetTable.java | 23 ++++++++----------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java index 791b02012b..77e9b82fc3 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java @@ -173,6 +173,8 @@ protected void onDeployment(final DeploymentEvent e) { if (isNotFromSelf(e)) { return; } + + LOG.debug("DeploymentEvent received. I will forward it to DMF broker."); sendMultiActionRequestMessages(e.getTenant(), e.getControllerIds()); } @@ -228,7 +230,7 @@ private DmfActionRequest createDmfActionRequest(final Target target, final Actio } - private DmfActionRequest createPlainActionRequest(final Action action) { + private static DmfActionRequest createPlainActionRequest(final Action action) { final DmfActionRequest actionRequest = new DmfActionRequest(); actionRequest.setActionId(action.getId()); return actionRequest; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index d949c7d86a..d35c2ffc16 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -362,7 +362,7 @@ private Set closeOrCancelActiveActions(final AbstractDsAssignmentStrategy final List> targetIdsChunks) { if (isMultiAssignmentsEnabled()) { - LOG.info(">>> multi-assignments are enabled: No need to close /cancel active actions."); + LOG.debug("Multi Assignments feature is enabled: No need to close /cancel active actions."); return Collections.emptySet(); } @@ -382,7 +382,7 @@ public void cancelInactiveScheduledActionsForTargets(final List targetIds) if (!isMultiAssignmentsEnabled()) { actionRepository.switchStatus(Status.CANCELED, targetIds, false, Status.SCHEDULED); } else { - LOG.info(">>> multi-assignments are enabled: No need to cancel inactive scheduled actions."); + LOG.debug("The Multi Assignments feature is enabled: No need to cancel inactive scheduled actions."); } } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java index 50d31fa8d1..58f47de43b 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java @@ -371,23 +371,18 @@ public AcceptCriterion getDropAcceptCriterion() { private Map prepareQueryConfigFilters() { final Map queryConfig = Maps.newHashMapWithExpectedSize(7); - managementUIState.getTargetTableFilters().getSearchText().ifPresent( + managementUIState.getTargetTableFilters().getSearchText() + .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); + managementUIState.getTargetTableFilters().getDistributionSet() + .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_DISTRIBUTION, value.getId())); + managementUIState.getTargetTableFilters().getPinnedDistId() + .ifPresent(value -> queryConfig.put(SPUIDefinitions.ORDER_BY_DISTRIBUTION, value)); + managementUIState.getTargetTableFilters().getTargetFilterQuery() + .ifPresent(value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TARGET_FILTER_QUERY, value)); - value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TEXT, value)); - managementUIState.getTargetTableFilters().getDistributionSet().ifPresent( - - value -> queryConfig.put(SPUIDefinitions.FILTER_BY_DISTRIBUTION, value.getId())); - managementUIState.getTargetTableFilters().getPinnedDistId().ifPresent( - - value -> queryConfig.put(SPUIDefinitions.ORDER_BY_DISTRIBUTION, value)); - managementUIState.getTargetTableFilters().getTargetFilterQuery().ifPresent( - - value -> queryConfig.put(SPUIDefinitions.FILTER_BY_TARGET_FILTER_QUERY, value)); queryConfig.put(SPUIDefinitions.FILTER_BY_NO_TAG, managementUIState.getTargetTableFilters().isNoTagSelected()); - if ( - - isFilteredByTags()) { + if (isFilteredByTags()) { final List list = new ArrayList<>(); list.addAll(managementUIState.getTargetTableFilters().getClickedTargetTags()); queryConfig.put(SPUIDefinitions.FILTER_BY_TAG, list.toArray(new String[list.size()])); From 20f733d58c20c79ff9e2e3c38833db91283bd7b8 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Fri, 5 Apr 2019 08:28:42 +0200 Subject: [PATCH 15/56] Multi Assignment support for cancellations Signed-off-by: Stefan Behl --- .../dmf/json/model/DmfMultiActionRequest.java | 14 +++++++------- .../repository/jpa/JpaDeploymentManagement.java | 7 ++++++- .../repository/jpa/OnlineDsAssignmentStrategy.java | 13 +++++++++++-- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java index 7e061e84e5..25ab98052b 100644 --- a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java +++ b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java @@ -41,9 +41,9 @@ public void addElement(final DmfMultiActionElement element) { } } - public void addElement(final EventTopic actionType, final DmfActionRequest action) { + public void addElement(final EventTopic topic, final DmfActionRequest action) { final DmfMultiActionElement element = new DmfMultiActionElement(); - element.setActionType(actionType); + element.setTopic(topic); element.setAction(action); addElement(element); } @@ -51,7 +51,7 @@ public void addElement(final EventTopic actionType, final DmfActionRequest actio public static class DmfMultiActionElement { @JsonProperty - private EventTopic actionType; + private EventTopic topic; @JsonProperty private DmfActionRequest action; @@ -64,12 +64,12 @@ public void setAction(final DmfActionRequest action) { this.action = action; } - public EventTopic getActionType() { - return actionType; + public EventTopic getTopic() { + return topic; } - public void setActionType(final EventTopic actionType) { - this.actionType = actionType; + public void setTopic(final EventTopic actionType) { + this.topic = actionType; } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index d35c2ffc16..9755eea4c8 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -442,7 +442,12 @@ public Action cancelAction(final long actionId) { actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, System.currentTimeMillis(), RepositoryConstants.SERVER_MESSAGE_PREFIX + "manual cancelation requested")); final Action saveAction = actionRepository.save(action); - onlineDsAssignmentStrategy.cancelAssignDistributionSetEvent(action.getTarget(), action.getId()); + + if (isMultiAssignmentsEnabled()) { + onlineDsAssignmentStrategy.sendDeploymentEvent(action.getTarget()); + } else { + onlineDsAssignmentStrategy.cancelAssignDistributionSetEvent(action.getTarget(), action.getId()); + } return saveAction; } else { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java index 615e7f1f1b..ac9c0d0c34 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java @@ -29,6 +29,7 @@ import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; +import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetUpdateStatus; import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.springframework.cloud.bus.BusProperties; @@ -59,6 +60,10 @@ void sendTargetUpdatedEvents(final DistributionSet set, final List ta }); } + void sendDeploymentEvent(final Target target) { + sendDeploymentEvent(target.getTenant(), Collections.singletonList(target.getControllerId())); + } + @Override void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult, final boolean deviceCanProcessMultipleActions) { @@ -84,8 +89,7 @@ void sendDeploymentEvents(final List assignment .collect(Collectors.toList()); if (!actions.isEmpty()) { final String tenant = actions.get(0).getTenant(); - afterCommit.afterCommit( - () -> eventPublisher.publishEvent(new DeploymentEvent(tenant, bus.getId(), controllerIds))); + sendDeploymentEvent(tenant, controllerIds); } } else { assignmentResults.forEach(this::sendDistributionSetAssignedEvent); @@ -161,4 +165,9 @@ private void sendTargetAssignDistributionSetEvent(final String tenant, final lon distributionSetId, actions, bus.getId(), actions.get(0).isMaintenanceWindowAvailable()))); } + private void sendDeploymentEvent(final String tenant, final List controllerIds) { + afterCommit.afterCommit( + () -> eventPublisher.publishEvent(new DeploymentEvent(tenant, bus.getId(), controllerIds))); + } + } From c7ab5108eed8abf8b71037f882b7767b2a1e1e8c Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Thu, 18 Apr 2019 15:25:07 +0200 Subject: [PATCH 16/56] fix permission problems and immutable lists Signed-off-by: Stefan Klotz --- .../amqp/AmqpMessageDispatcherService.java | 18 ++++------ .../amqp/AmqpMessageHandlerService.java | 34 +++++++++++++++++-- .../dmf/json/model/DmfMultiActionRequest.java | 9 +++-- .../repository/ControllerManagement.java | 3 ++ .../event/remote/DeploymentEvent.java | 4 +-- .../jpa/JpaControllerManagement.java | 8 +++++ 6 files changed, 55 insertions(+), 21 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java index 77e9b82fc3..3f54773b27 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java @@ -142,20 +142,14 @@ protected void targetAssignDistributionSet(final TargetAssignDistributionSetEven LOG.debug("targetAssignDistributionSet retrieved. I will forward it to DMF broker."); - distributionSetManagement.get(assignedEvent.getDistributionSetId()).ifPresent(set -> { + distributionSetManagement.get(assignedEvent.getDistributionSetId()).ifPresent(ds -> { - final Map> modules = Maps - .newHashMapWithExpectedSize(set.getModules().size()); - set.getModules() - .forEach( - module -> modules.put(module, - softwareModuleManagement.findMetaDataBySoftwareModuleIdAndTargetVisible( - PageRequest.of(0, RepositoryConstants.MAX_META_DATA_COUNT), module.getId()) - .getContent())); + final Map> softwareModules = getSoftwareModulesWithMetadata( + ds); targetManagement.getByControllerID(assignedEvent.getActions().keySet()) .forEach(target -> sendUpdateMessageToTarget(assignedEvent.getTenant(), target, - assignedEvent.getActions().get(target.getControllerId()), modules, + assignedEvent.getActions().get(target.getControllerId()), softwareModules, assignedEvent.isMaintenanceWindowAvailable())); }); @@ -189,7 +183,7 @@ protected void sendMultiActionRequestMessages(final String tenant, final List { final DistributionSet distributionSet = action.getDistributionSet(); softwareModuleMetadata.computeIfAbsent(distributionSet.getId(), - id -> getSoftwareModuleMetadata(distributionSet)); + id -> getSoftwareModulesWithMetadata(distributionSet)); }); if (!activeActions.isEmpty()) { @@ -474,7 +468,7 @@ private DmfArtifact convertArtifact(final Target target, final Artifact localArt return artifact; } - private Map> getSoftwareModuleMetadata( + private Map> getSoftwareModulesWithMetadata( final DistributionSet distributionSet) { final Map> moduleMetadata = Maps .newHashMapWithExpectedSize(distributionSet.getModules().size()); diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java index e8b16fce12..4ea3e90f55 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java @@ -13,9 +13,11 @@ import java.io.Serializable; import java.net.URI; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -35,6 +37,7 @@ import org.eclipse.hawkbit.repository.builder.ActionStatusCreate; import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action.Status; +import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.SoftwareModule; import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata; import org.eclipse.hawkbit.repository.model.Target; @@ -46,6 +49,7 @@ import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.data.domain.PageRequest; import org.springframework.messaging.handler.annotation.Header; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; @@ -78,6 +82,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService { private final SystemSecurityContext systemSecurityContext; + private static final int MAX_ACTIONS = 100; + /** * Constructor. * @@ -208,13 +214,37 @@ private void registerTarget(final Message message, final String virtualHost) { private void checkIfUpdatesAvailable(final Target target) { if (isMultiAssignmentsEnabled()) { - amqpMessageDispatcherService.sendMultiActionRequestMessages(target.getTenant(), - Collections.singletonList(target.getControllerId())); + final List actions = controllerManagement + .findActiveActionsByTarget(PageRequest.of(0, MAX_ACTIONS), target.getControllerId()).getContent(); + + final Set distributionSets = actions.stream().map(Action::getDistributionSet) + .collect(Collectors.toSet()); + final Map>> softwareModulesPerDistributionSet = distributionSets + .stream().collect(Collectors.toMap(DistributionSet::getId, this::getSoftwareModulesWithMetadata)); + + amqpMessageDispatcherService.sendMultiActionRequestToTarget(target.getTenant(), target, actions, + softwareModulesPerDistributionSet); } else { checkIfUpdateAvailable(target); } } + private Map> getSoftwareModulesWithMetadata( + final DistributionSet distributionSet) { + final Map softwareModules = distributionSet.getModules().stream() + .collect(Collectors.toMap(SoftwareModule::getId, sm -> sm)); + + final Map> metadataBySm = controllerManagement + .findTargetVisibleMetaDataBySoftwareModuleId(softwareModules.keySet()); + + final Map> softwareModulesWithMetadata = new HashMap<>(); + softwareModules.forEach((id, sm) -> { + final List metadata = metadataBySm.get(id); + softwareModulesWithMetadata.put(sm, metadata != null ? metadata : Collections.emptyList()); + }); + return softwareModulesWithMetadata; + } + private void checkIfUpdateAvailable(final Target target) { final Optional actionOptional = controllerManagement .findOldestActiveActionByTarget(target.getControllerId()); diff --git a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java index 25ab98052b..93308c0d7d 100644 --- a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java +++ b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.dmf.json.model; -import java.util.Arrays; +import java.util.ArrayList; import java.util.List; import org.eclipse.hawkbit.dmf.amqp.api.EventTopic; @@ -34,11 +34,10 @@ public List getElements() { } public void addElement(final DmfMultiActionElement element) { - if (elements != null) { - elements.add(element); - } else { - elements = Arrays.asList(element); + if (elements == null) { + elements = new ArrayList<>(); } + elements.add(element); } public void addElement(final EventTopic topic, final DmfActionRequest action) { diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java index 6f1910f385..cf05b0b56a 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java @@ -151,6 +151,9 @@ Map> findTargetVisibleMetaDataBySoftwareModul @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) Optional findOldestActiveActionByTarget(@NotEmpty String controllerId); + @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) + Page findActiveActionsByTarget(@NotNull Pageable pageable, @NotEmpty String controllerId); + /** * Get the {@link Action} entity for given actionId with all lazy * attributes. diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java index f88612c8a4..9edf0395b2 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java @@ -8,7 +8,7 @@ */ package org.eclipse.hawkbit.repository.event.remote; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -20,7 +20,7 @@ public class DeploymentEvent extends RemoteTenantAwareEvent implements Iterable< private static final long serialVersionUID = 1L; - private final List controllerIds = Arrays.asList(); + private final List controllerIds = new ArrayList<>(); /** * Default constructor. diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java index e914c0a3cc..fe298697a6 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java @@ -355,6 +355,14 @@ public Optional findOldestActiveActionByTarget(final String controllerId true); } + @Override + public Page findActiveActionsByTarget(final Pageable pageable, final String controllerId) { + if (!actionRepository.activeActionExistsForControllerId(controllerId)) { + return Page.empty(); + } + return actionRepository.findByActiveAndTarget(pageable, controllerId, true); + } + @Override public Optional findActionWithDetails(final long actionId) { return actionRepository.getById(actionId); From ef38921aff763c3d87d9d9a93a4afc2b7e21895b Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Tue, 30 Apr 2019 11:14:13 +0200 Subject: [PATCH 17/56] add message dispatcher tests for outgoing multiassignment messages, fix old tests Signed-off-by: Stefan Klotz --- .../amqp/AmqpMessageDispatcherService.java | 2 +- .../amqp/AmqpMessageHandlerServiceTest.java | 3 + .../AbstractAmqpServiceIntegrationTest.java | 65 ++++++- ...ssageDispatcherServiceIntegrationTest.java | 173 +++++++++++++++--- ...pMessageHandlerServiceIntegrationTest.java | 6 +- .../integration/listener/ReplyToListener.java | 28 ++- .../dmf/json/model/DmfMultiActionRequest.java | 9 + 7 files changed, 245 insertions(+), 41 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java index aea80b7bab..c6835c5daf 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java @@ -198,7 +198,7 @@ protected void sendMultiActionRequestToTarget(final String tenant, final Target final Map>> softwareModulesPerDistributionSet) { final URI targetAdress = target.getAddress(); - if (!IpUtil.isAmqpUri(targetAdress)) { + if (!IpUtil.isAmqpUri(targetAdress) || actions == null || actions.isEmpty()) { return; } diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java index 677e67eca6..c8ec04b60e 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java @@ -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; @@ -138,6 +139,8 @@ public void before() throws Exception { messageConverter = new Jackson2JsonMessageConverter(); when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter); when(artifactManagementMock.findFirstBySHA1(SHA1)).thenReturn(Optional.empty()); + when(tenantConfigurationManagement.getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class, + Boolean.TRUE)).thenReturn(false); final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(); final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware); diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java index db5efde7d8..da3f034801 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java @@ -11,11 +11,16 @@ import static org.assertj.core.api.Assertions.assertThat; import java.nio.charset.StandardCharsets; +import java.util.AbstractMap.SimpleEntry; +import java.util.Collections; +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.amqp.DmfApiConfiguration; import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings; @@ -28,6 +33,8 @@ import org.eclipse.hawkbit.dmf.json.model.DmfAttributeUpdate; 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.DmfMultiActionRequest.DmfMultiActionElement; import org.eclipse.hawkbit.integration.listener.DeadletterListener; import org.eclipse.hawkbit.integration.listener.ReplyToListener; import org.eclipse.hawkbit.matcher.SoftwareModuleJsonMatcher; @@ -87,6 +94,7 @@ public void initListener() { replyToListener = harness.getSpy(ReplyToListener.LISTENER_ID); assertThat(replyToListener).isNotNull(); Mockito.reset(replyToListener); + replyToListener.purge(); getDmfClient().setExchange(AmqpSettings.DMF_EXCHANGE); } @@ -102,6 +110,20 @@ protected T waitUntilIsPresent(final Callable> callable) { } } + protected void waitUntilEventMessagesAreSent(final int numberOfMessages) { + createConditionFactory().until(() -> { + int messagesReceived = 0; + for (final List messages : replyToListener.getEventMessages().values()) { + messagesReceived += messages.size(); + } + return messagesReceived == numberOfMessages; + }); + } + + protected void purgeReplyToListener() { + replyToListener.purge(); + } + protected void verifyDeadLetterMessages(final int expectedMessages) { createConditionFactory().untilAsserted(() -> { Mockito.verify(getDeadletterListener(), Mockito.times(expectedMessages)).handleMessage(Mockito.any()); @@ -158,7 +180,7 @@ protected void assertRequestAttributesUpdateMessage(final String target) { } 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 +218,39 @@ private void assertAssignmentMessage(final Set dsModules, final assertThat(updatedTarget.getSecurityToken()).isEqualTo(downloadAndUpdateRequest.getTargetSecurityToken()); } - protected void assertDownloadAndInstallMessage(final Set dsModules, final String controllerId) { - assertAssignmentMessage(dsModules, controllerId, EventTopic.DOWNLOAD_AND_INSTALL); + protected void assertDownloadAndInstallMessage(final Set softwareModules, + final String controllerId) { + assertAssignmentMessage(softwareModules, controllerId, EventTopic.DOWNLOAD_AND_INSTALL); + + } + + protected void assertLatestMultiActionMessage(final String controllerId, + final List> actionsExpected) { + final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION); + assertThat(multiactionMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID)) + .isEqualTo(controllerId); + + final DmfMultiActionRequest dmfMultiActionRequest = (DmfMultiActionRequest) getDmfClient().getMessageConverter() + .fromMessage(multiactionMessage); + + final List multiActionRequest = dmfMultiActionRequest.getElements(); + final List> actionsFromMessage = multiActionRequest.stream() + .map(request -> new SimpleEntry<>(request.getAction().getActionId(), request.getTopic())) + .collect(Collectors.toList()); + assertThat(actionsFromMessage).containsExactlyElementsOf(actionsExpected); + } + + protected void assertLatestMultiActionMessage(final String controllerId, final long actionId, + final EventTopic topic) { + final SimpleEntry action = new SimpleEntry<>(actionId, topic); + assertLatestMultiActionMessage(controllerId, Collections.singletonList(action)); } protected void assertDownloadMessage(final Set 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); } @@ -238,7 +284,8 @@ protected void assertAllTargetsCount(final long expectedTargetsCount) { 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 headers = replyMessage.getMessageProperties().getHeaders(); assertThat(headers.get(MessageHeaderKey.TOPIC)).isEqualTo(eventTopic.toString()); @@ -264,7 +311,7 @@ protected void registerAndAssertTargetWithExistingTenant(final String target, 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 +320,7 @@ protected void registerAndAssertTargetWithExistingTenant(final String target, 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 +362,7 @@ protected Message createPingMessage(final String correlationId, final String ten 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 +370,7 @@ protected Message createActionStatusUpdateMessage(final String target, final Str 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) { diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java index 39ecb780d4..8280596486 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java @@ -8,6 +8,13 @@ */ 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.Map; import java.util.Optional; import java.util.UUID; @@ -15,6 +22,7 @@ import org.eclipse.hawkbit.dmf.amqp.api.EventTopic; import org.eclipse.hawkbit.dmf.json.model.DmfActionStatus; +import org.eclipse.hawkbit.repository.event.remote.DeploymentEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent; @@ -34,6 +42,7 @@ 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 +51,6 @@ 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 { @@ -133,12 +137,140 @@ public void assignDistributionSetMultipleTimes() { getDistributionSet().getModules(), controllerId); assertCancelActionMessage(assignmentResult.getActionIds().get(0), controllerId); - createAndSendTarget(controllerId, TENANT_EXIST); + createAndSendThingCreated(controllerId, TENANT_EXIST); waitUntilTargetHasStatus(controllerId, TargetUpdateStatus.PENDING); assertCancelActionMessage(assignmentResult.getActionIds().get(0), controllerId); } + @Test + @Description("If multi assignment is enabled multi-action message are sent.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), + @Expect(type = DeploymentEvent.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() { + setMultiAssignmentsEnabled(true); + final String controllerId = TARGET_PREFIX + "assignMultipleDsInMultiAssignMode"; + registerAndAssertTargetWithExistingTenant(controllerId); + + final Long actionId1 = assignNewDsToTarget(controllerId); + final SimpleEntry action1Install = new SimpleEntry(actionId1, + EventTopic.DOWNLOAD_AND_INSTALL); + waitUntilEventMessagesAreSent(1); + assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Install)); + + final Long actionId2 = assignNewDsToTarget(controllerId); + final SimpleEntry action2Install = new SimpleEntry(actionId2, + EventTopic.DOWNLOAD_AND_INSTALL); + waitUntilEventMessagesAreSent(2); + 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 = DeploymentEvent.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 = 2) }) + public void cancelActionInMultiAssignMode() { + setMultiAssignmentsEnabled(true); + final String controllerId = TARGET_PREFIX + "cancelActionInMultiAssignMode"; + registerAndAssertTargetWithExistingTenant(controllerId); + + final long actionId1 = assignNewDsToTarget(controllerId); + final long actionId2 = assignNewDsToTarget(controllerId); + waitUntilEventMessagesAreSent(2); + deploymentManagement.cancelAction(actionId1); + waitUntilEventMessagesAreSent(3); + + final SimpleEntry action1Cancel = new SimpleEntry(actionId1, + EventTopic.CANCEL_DOWNLOAD); + final SimpleEntry action2Install = new SimpleEntry(actionId2, + EventTopic.DOWNLOAD_AND_INSTALL); + + assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Cancel, action2Install)); + updateActionViaDmfClient(controllerId, actionId1, DmfActionStatus.CANCELED); + createAndSendThingCreated(controllerId, TENANT_EXIST); + waitUntilEventMessagesAreSent(4); + assertLatestMultiActionMessage(controllerId, Arrays.asList(action2Install)); + } + + @Test + @Description("Handle finishing an action in multi assignment mode.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), + @Expect(type = DeploymentEvent.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 = 2) }) + public void finishActionInMultiAssignMode() { + setMultiAssignmentsEnabled(true); + final String controllerId = TARGET_PREFIX + "finishActionInMultiAssignMode"; + registerAndAssertTargetWithExistingTenant(controllerId); + + final long actionId1 = assignNewDsToTarget(controllerId); + final long actionId2 = assignNewDsToTarget(controllerId); + waitUntilEventMessagesAreSent(2); + + updateActionViaDmfClient(controllerId, actionId1, DmfActionStatus.FINISHED); + waitUntilEventMessagesAreSent(3); + assertRequestAttributesUpdateMessage(controllerId); + + createAndSendThingCreated(controllerId, TENANT_EXIST); + waitUntilEventMessagesAreSent(4); + assertLatestMultiActionMessage(controllerId, actionId2, EventTopic.DOWNLOAD_AND_INSTALL); + } + + @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 = DeploymentEvent.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() { + setMultiAssignmentsEnabled(true); + 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); + waitUntilEventMessagesAreSent(1); + final Long actionId2 = assignDistributionSet(ds.getId(), controllerId).getActionIds().get(0); + waitUntilEventMessagesAreSent(2); + + final SimpleEntry action1Install = new SimpleEntry<>(actionId1, + EventTopic.DOWNLOAD_AND_INSTALL); + final SimpleEntry 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()); + return assignDistributionSet(ds.getId(), controllerId).getActionIds().get(0); + } + @Test @Description("Verify that a cancel assignment send a cancel message.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @@ -154,7 +286,7 @@ public void sendCancelStatus() { final Long actionId = registerTargetAndCancelActionId(controllerId); - createAndSendTarget(controllerId, TENANT_EXIST); + createAndSendThingCreated(controllerId, TENANT_EXIST); waitUntilTargetHasStatus(controllerId, TargetUpdateStatus.PENDING); assertCancelActionMessage(actionId, controllerId); } @@ -177,33 +309,23 @@ public void sendDeleteMessage() { @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).getActionIds().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).getActionIds().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); } @@ -249,4 +371,9 @@ private void waitUntilTargetHasStatus(final String controllerId, final TargetUpd private void waitUntil(final Callable callable) { createConditionFactory().until(() -> securityRule.runAsPrivileged(callable)); } + + private void setMultiAssignmentsEnabled(final boolean enable) { + tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, + enable); + } } diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageHandlerServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageHandlerServiceIntegrationTest.java index 56b10edb38..4be78fbfa8 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageHandlerServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageHandlerServiceIntegrationTest.java @@ -104,7 +104,7 @@ public void registerTargets() { @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 void registerEmptyTarget() { @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 void registerWhitespaceTarget() { @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(); diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java index 6218789c21..2af41c7f46 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java @@ -10,8 +10,12 @@ import static org.junit.Assert.fail; +import java.nio.charset.StandardCharsets; +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 +30,24 @@ public class ReplyToListener implements TestRabbitListener { public static final String LISTENER_ID = "replyto"; public static final String REPLY_TO_QUEUE = "reply_queue"; - private final Map eventTopicMessages = new EnumMap<>(EventTopic.class); + private final Map> eventMessages = new EnumMap<>(EventTopic.class); private final Map deleteMessages = new HashMap<>(); private final Map 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); + eventMessages.merge(eventTopic, Collections.singletonList(message), (oldList, listToAdd) -> { + final List newList = new ArrayList<>(oldList); + newList.addAll(listToAdd); + return newList; + }); return; } @@ -62,8 +69,19 @@ public void handleMessage(final Message message) { } - public Map getEventTopicMessages() { - return eventTopicMessages; + public void purge() { + eventMessages.clear(); + deleteMessages.clear(); + pingResponseMessages.clear(); + } + + public Message getLatestEventMessage(final EventTopic eventTopic) { + final List messages = getEventMessages().get(eventTopic); + return messages == null ? null : messages.get(messages.size() - 1); + } + + public Map> getEventMessages() { + return eventMessages; } public Map getDeleteMessages() { diff --git a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java index 93308c0d7d..20498b74b1 100644 --- a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java +++ b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java @@ -13,6 +13,7 @@ import org.eclipse.hawkbit.dmf.amqp.api.EventTopic; +import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; @@ -28,6 +29,14 @@ public class DmfMultiActionRequest { private List elements; + public DmfMultiActionRequest() { + } + + @JsonCreator + public DmfMultiActionRequest(final List elements) { + this.elements = elements; + } + @JsonValue public List getElements() { return elements; From 9d9adbb471c59983c9f0727de93a5beee9877586 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Thu, 2 May 2019 16:12:26 +0200 Subject: [PATCH 18/56] Implement Multi-Assignment support for rollout groups Signed-off-by: Stefan Behl --- .../jpa/JpaDeploymentManagement.java | 11 ++-- .../jpa/OnlineDsAssignmentStrategy.java | 51 ++++++++++++------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index 9755eea4c8..45abc889a8 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -89,7 +89,6 @@ import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.CollectionUtils; import org.springframework.validation.annotation.Validated; import com.google.common.collect.Lists; @@ -507,18 +506,14 @@ private long startScheduledActionsByRolloutGroupParentInNewTransaction(final Lon return 0L; } - final String tenant = rolloutGroupActions.getContent().get(0).getTenant(); - final boolean maintenanceWindowAvailable = rolloutGroupActions.getContent().get(0) - .isMaintenanceWindowAvailable(); - final List targetAssignments = rolloutGroupActions.getContent().stream() .map(action -> (JpaAction) action).map(this::closeActionIfSetWasAlreadyAssigned) .filter(Objects::nonNull).map(this::startScheduledActionIfNoCancelationHasToBeHandledFirst) .filter(Objects::nonNull).collect(Collectors.toList()); - if (!CollectionUtils.isEmpty(targetAssignments)) { - afterCommit.afterCommit(() -> eventPublisher.publishEvent(new TargetAssignDistributionSetEvent(tenant, - distributionSetId, targetAssignments, bus.getId(), maintenanceWindowAvailable))); + if (!targetAssignments.isEmpty()) { + onlineDsAssignmentStrategy.sendDeploymentEvents(distributionSetId, targetAssignments, + isMultiAssignmentsEnabled()); } return rolloutGroupActions.getTotalElements(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java index ac9c0d0c34..69ce5696d5 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java @@ -77,24 +77,42 @@ void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult @Override void sendDeploymentEvents(final List assignmentResults, final boolean deviceCanProcessMultipleActions) { - - final List actions = assignmentResults.stream().flatMap(result -> result.getActions().stream()) - .filter(action -> { - final Status actionStatus = action.getStatus(); - return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; - }).collect(Collectors.toList()); - if (deviceCanProcessMultipleActions) { - final List controllerIds = actions.stream().map(action -> action.getTarget().getControllerId()) - .collect(Collectors.toList()); - if (!actions.isEmpty()) { - final String tenant = actions.get(0).getTenant(); - sendDeploymentEvent(tenant, controllerIds); - } + sendDeploymentEvent(assignmentResults.stream().flatMap(result -> result.getActions().stream()) + .collect(Collectors.toList())); } else { assignmentResults.forEach(this::sendDistributionSetAssignedEvent); } + } + void sendDeploymentEvents(final long distributionSetId, final List actions, + final boolean deviceCanProcessMultipleActions) { + if (deviceCanProcessMultipleActions) { + sendDeploymentEvent(actions); + } else { + final List filteredActions = actions.stream().filter(action -> { + final Status actionStatus = action.getStatus(); + return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; + }).collect(Collectors.toList()); + if (filteredActions.isEmpty()) { + return; + } + sendTargetAssignDistributionSetEvent(filteredActions.get(0).getTenant(), distributionSetId, + filteredActions); + } + } + + private void sendDeploymentEvent(final List actions) { + final List filteredActions = actions.stream().filter(action -> { + final Status actionStatus = action.getStatus(); + return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; + }).collect(Collectors.toList()); + if (filteredActions.isEmpty()) { + return; + } + final String tenant = filteredActions.get(0).getTenant(); + sendDeploymentEvent(tenant, filteredActions.stream().map(action -> action.getTarget().getControllerId()) + .collect(Collectors.toList())); } @Override @@ -143,15 +161,12 @@ JpaActionStatus createActionStatus(final JpaAction action, final String actionMe private DistributionSetAssignmentResult sendDistributionSetAssignedEvent( final DistributionSetAssignmentResult assignmentResult) { - - final List filtered = assignmentResult.getActions().stream().filter(action -> { + final List filteredActions = assignmentResult.getActions().stream().filter(action -> { final Status actionStatus = action.getStatus(); return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; }).collect(Collectors.toList()); - final DistributionSet set = assignmentResult.getDistributionSet(); - sendTargetAssignDistributionSetEvent(set.getTenant(), set.getId(), filtered); - + sendTargetAssignDistributionSetEvent(set.getTenant(), set.getId(), filteredActions); return assignmentResult; } From 7f30eed6352d9f69eb653bce7e4af76899bc656d Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Thu, 2 May 2019 16:23:54 +0200 Subject: [PATCH 19/56] Minor changes Signed-off-by: Stefan Behl --- .../hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java | 1 - 1 file changed, 1 deletion(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java index 2f798c80cb..45ffc8199e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java @@ -118,7 +118,6 @@ abstract void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet * * @param targetIds * to cancel actions for - * @return {@link Set} of {@link Target#getId()}s */ abstract void closeActiveActions(List> targetIds); From efde30e899f69c2d40bb4801802ca00e772aac32 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Thu, 2 May 2019 16:39:46 +0200 Subject: [PATCH 20/56] Refactoring Signed-off-by: Stefan Behl --- .../jpa/AbstractDsAssignmentStrategy.java | 17 ++++++++++++----- .../jpa/JpaDeploymentManagement.java | 14 +++++++------- .../jpa/OfflineDsAssignmentStrategy.java | 12 ++++++------ .../jpa/OnlineDsAssignmentStrategy.java | 18 +++++++++--------- 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java index 45ffc8199e..57331fcf99 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java @@ -13,6 +13,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Collectors; import org.eclipse.hawkbit.repository.QuotaManagement; @@ -54,10 +55,13 @@ public abstract class AbstractDsAssignmentStrategy { private final ActionStatusRepository actionStatusRepository; private final QuotaManagement quotaManagement; + private final Supplier multiAssignmentsConfig; + AbstractDsAssignmentStrategy(final TargetRepository targetRepository, final AfterTransactionCommitExecutor afterCommit, final ApplicationEventPublisher eventPublisher, final BusProperties bus, final ActionRepository actionRepository, - final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement) { + final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement, + final Supplier multiAssignmentsConfig) { this.targetRepository = targetRepository; this.afterCommit = afterCommit; this.eventPublisher = eventPublisher; @@ -65,6 +69,7 @@ public abstract class AbstractDsAssignmentStrategy { this.actionRepository = actionRepository; this.actionStatusRepository = actionStatusRepository; this.quotaManagement = quotaManagement; + this.multiAssignmentsConfig = multiAssignmentsConfig; } /** @@ -121,11 +126,9 @@ abstract void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet */ abstract void closeActiveActions(List> targetIds); - abstract void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult, - final boolean deviceCanProcessMultipleActions); + abstract void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult); - abstract void sendDeploymentEvents(final List assignmentResults, - final boolean deviceCanProcessMultipleActions); + abstract void sendDeploymentEvents(final List assignmentResults); protected void sendTargetUpdatedEvent(final JpaTarget target) { afterCommit.afterCommit(() -> eventPublisher.publishEvent(new TargetUpdatedEvent(target, bus.getId()))); @@ -239,6 +242,10 @@ JpaActionStatus createActionStatus(final JpaAction action, final String actionMe return actionStatus; } + boolean isMultiAssignmentsEnabled() { + return multiAssignmentsConfig.get(); + } + private void assertActionsPerTargetQuota(final Target target, final int requested) { final int quota = quotaManagement.getMaxActionsPerTarget(); QuotaHelper.assertAssignmentQuota(target.getId(), requested, quota, Action.class, Target.class, diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index 45abc889a8..72df145b1c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -160,9 +160,9 @@ protected JpaDeploymentManagement(final EntityManager entityManager, final Actio this.virtualPropertyReplacer = virtualPropertyReplacer; this.txManager = txManager; onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, afterCommit, eventPublisher, bus, - actionRepository, actionStatusRepository, quotaManagement); + actionRepository, actionStatusRepository, quotaManagement, this::isMultiAssignmentsEnabled); offlineDsAssignmentStrategy = new OfflineDsAssignmentStrategy(targetRepository, afterCommit, eventPublisher, - bus, actionRepository, actionStatusRepository, quotaManagement); + bus, actionRepository, actionStatusRepository, quotaManagement, this::isMultiAssignmentsEnabled); this.tenantConfigurationManagement = tenantConfigurationManagement; this.quotaManagement = quotaManagement; this.systemSecurityContext = systemSecurityContext; @@ -181,7 +181,7 @@ public DistributionSetAssignmentResult offlineAssignedDistributionSet(final Long .map(controllerId -> new TargetWithActionType(controllerId, ActionType.FORCED, -1)) .collect(Collectors.toList()), null, offlineDsAssignmentStrategy); - offlineDsAssignmentStrategy.sendDeploymentEvents(result, isMultiAssignmentsEnabled()); + offlineDsAssignmentStrategy.sendDeploymentEvents(result); return result; } @@ -197,7 +197,7 @@ public DistributionSetAssignmentResult assignDistributionSet(final long dsID, fi .map(controllerId -> new TargetWithActionType(controllerId, actionType, forcedTimestamp)) .collect(Collectors.toList()), null, onlineDsAssignmentStrategy); - onlineDsAssignmentStrategy.sendDeploymentEvents(result, isMultiAssignmentsEnabled()); + onlineDsAssignmentStrategy.sendDeploymentEvents(result); return result; } @@ -210,7 +210,7 @@ public DistributionSetAssignmentResult assignDistributionSet(final long dsID, final DistributionSetAssignmentResult result = assignDistributionSetToTargets(dsID, targets, null, onlineDsAssignmentStrategy); - onlineDsAssignmentStrategy.sendDeploymentEvents(result, isMultiAssignmentsEnabled()); + onlineDsAssignmentStrategy.sendDeploymentEvents(result); return result; } @@ -221,7 +221,7 @@ public List assignDistributionSets(final Set results = dsIDs.stream() .map(dsID -> assignDistributionSetToTargets(dsID, targets, null, onlineDsAssignmentStrategy)) .collect(Collectors.toList()); - onlineDsAssignmentStrategy.sendDeploymentEvents(results, isMultiAssignmentsEnabled()); + onlineDsAssignmentStrategy.sendDeploymentEvents(results); return results; } @@ -234,7 +234,7 @@ public DistributionSetAssignmentResult assignDistributionSet(final long dsID, final DistributionSetAssignmentResult result = assignDistributionSetToTargets(dsID, targets, actionMessage, onlineDsAssignmentStrategy); - onlineDsAssignmentStrategy.sendDeploymentEvents(result, isMultiAssignmentsEnabled()); + onlineDsAssignmentStrategy.sendDeploymentEvents(result); return result; } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java index ac531087b9..0bd91f2e89 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Collectors; import org.eclipse.hawkbit.repository.QuotaManagement; @@ -45,9 +46,10 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { OfflineDsAssignmentStrategy(final TargetRepository targetRepository, final AfterTransactionCommitExecutor afterCommit, final ApplicationEventPublisher eventPublisher, final BusProperties bus, final ActionRepository actionRepository, - final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement) { + final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement, + final Supplier multiAssignmentsConfig) { super(targetRepository, afterCommit, eventPublisher, bus, actionRepository, actionStatusRepository, - quotaManagement); + quotaManagement, multiAssignmentsConfig); } @Override @@ -104,14 +106,12 @@ protected JpaActionStatus createActionStatus(final JpaAction action, final Strin } @Override - void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult, - final boolean deviceCanProcessMultipleActions) { + void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult) { // no need to send deployment events in the offline case } @Override - void sendDeploymentEvents(final List assignmentResults, - final boolean deviceCanProcessMultipleActions) { + void sendDeploymentEvents(final List assignmentResults) { // no need to send deployment events in the offline case } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java index 69ce5696d5..815d719127 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Collectors; import org.eclipse.hawkbit.repository.QuotaManagement; @@ -47,9 +48,10 @@ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { OnlineDsAssignmentStrategy(final TargetRepository targetRepository, final AfterTransactionCommitExecutor afterCommit, final ApplicationEventPublisher eventPublisher, final BusProperties bus, final ActionRepository actionRepository, - final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement) { + final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement, + final Supplier multiAssignmentsConfig) { super(targetRepository, afterCommit, eventPublisher, bus, actionRepository, actionStatusRepository, - quotaManagement); + quotaManagement, multiAssignmentsConfig); } @Override @@ -65,19 +67,17 @@ void sendDeploymentEvent(final Target target) { } @Override - void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult, - final boolean deviceCanProcessMultipleActions) { - if (deviceCanProcessMultipleActions) { - sendDeploymentEvents(Collections.singletonList(assignmentResult), deviceCanProcessMultipleActions); + void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult) { + if (isMultiAssignmentsEnabled()) { + sendDeploymentEvents(Collections.singletonList(assignmentResult)); } else { sendDistributionSetAssignedEvent(assignmentResult); } } @Override - void sendDeploymentEvents(final List assignmentResults, - final boolean deviceCanProcessMultipleActions) { - if (deviceCanProcessMultipleActions) { + void sendDeploymentEvents(final List assignmentResults) { + if (isMultiAssignmentsEnabled()) { sendDeploymentEvent(assignmentResults.stream().flatMap(result -> result.getActions().stream()) .collect(Collectors.toList())); } else { From cc4e5d077a2429a78244252ce1b5f433f9745455 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Fri, 3 May 2019 07:24:37 +0200 Subject: [PATCH 21/56] Register new deployment event with protobuff framework Signed-off-by: Stefan Behl --- .../src/main/java/org/eclipse/hawkbit/event/EventType.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventType.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventType.java index fe904aedd5..4c0f5951c6 100644 --- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventType.java +++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventType.java @@ -13,6 +13,7 @@ import java.util.Map.Entry; import java.util.Optional; +import org.eclipse.hawkbit.repository.event.remote.DeploymentEvent; import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.DistributionSetTagDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.DistributionSetTypeDeletedEvent; @@ -132,6 +133,9 @@ public class EventType { // target attributes requested flag TYPES.put(37, TargetAttributesRequestedEvent.class); + + // generic deployment event for assignments and cancellations + TYPES.put(38, DeploymentEvent.class); } private int value; From 018800d00a1964a8633cb3ad041ac66237fc9cac Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Fri, 3 May 2019 07:29:37 +0200 Subject: [PATCH 22/56] Allow same DS to be assigned multiple times Signed-off-by: Stefan Behl --- .../repository/jpa/OnlineDsAssignmentStrategy.java | 12 +++++++++--- .../hawkbit/repository/jpa/TargetRepository.java | 11 +++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java index 815d719127..5a89d2d685 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -117,9 +118,14 @@ private void sendDeploymentEvent(final List actions) { @Override List findTargetsForAssignment(final List controllerIDs, final long setId) { - return Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream() - .map(ids -> targetRepository - .findAll(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, setId))) + final Function, List> mapper; + if (isMultiAssignmentsEnabled()) { + mapper = targetRepository::findAllByControllerId; + } else { + mapper = ids -> targetRepository + .findAll(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, setId)); + } + return Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream().map(mapper) .flatMap(List::stream).collect(Collectors.toList()); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetRepository.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetRepository.java index 6bc0d2dac2..1f9caf620e 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetRepository.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/TargetRepository.java @@ -223,6 +223,17 @@ List findByTagNameAndControllerIdIn(@Param("tagname") String tag, @Query("SELECT t FROM JpaTarget t WHERE t.id IN ?1") List findAllById(Iterable ids); + /** + * Finds all targets for the given list of controller IDs. + * + * @param controllerIds + * The controller IDs to look for. + * + * @return The list of matching targets. + */ + @Query("SELECT t FROM JpaTarget t WHERE t.controllerId IN ?1") + List findAllByControllerId(Iterable controllerIds); + /** * * Finds all targets of a rollout group. From c4c5fae2bdb4552bf2790234507b7cabbb8b0c50 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Fri, 3 May 2019 10:18:52 +0200 Subject: [PATCH 23/56] Fix assignment with pending cancellations Signed-off-by: Stefan Behl --- .../hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java | 5 +++++ .../hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java index 57331fcf99..c3fe7d61a2 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java @@ -242,6 +242,11 @@ JpaActionStatus createActionStatus(final JpaAction action, final String actionMe return actionStatus; } + boolean hasPendingCancellations(final Target target) { + return actionRepository.findByActiveAndTarget(null, target.getControllerId(), true).getContent().stream() + .anyMatch(action -> action.getStatus() == Status.CANCELING); + } + boolean isMultiAssignmentsEnabled() { return multiAssignmentsConfig.get(); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java index 5a89d2d685..65af94a680 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java @@ -169,7 +169,8 @@ private DistributionSetAssignmentResult sendDistributionSetAssignedEvent( final DistributionSetAssignmentResult assignmentResult) { final List filteredActions = assignmentResult.getActions().stream().filter(action -> { final Status actionStatus = action.getStatus(); - return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; + return !hasPendingCancellations(action.getTarget()) && Status.CANCELING != actionStatus + && Status.CANCELED != actionStatus; }).collect(Collectors.toList()); final DistributionSet set = assignmentResult.getDistributionSet(); sendTargetAssignDistributionSetEvent(set.getTenant(), set.getId(), filteredActions); From eb09c546335691d27a2540192b4eca6da98058f3 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Fri, 3 May 2019 11:58:27 +0200 Subject: [PATCH 24/56] Reduce repository /DB calls Signed-off-by: Stefan Behl --- .../repository/jpa/AbstractDsAssignmentStrategy.java | 5 ----- .../hawkbit/repository/jpa/JpaDeploymentManagement.java | 7 +++++-- .../hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java | 3 +-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java index c3fe7d61a2..57331fcf99 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java @@ -242,11 +242,6 @@ JpaActionStatus createActionStatus(final JpaAction action, final String actionMe return actionStatus; } - boolean hasPendingCancellations(final Target target) { - return actionRepository.findByActiveAndTarget(null, target.getControllerId(), true).getContent().stream() - .anyMatch(action -> action.getStatus() == Status.CANCELING); - } - boolean isMultiAssignmentsEnabled() { return multiAssignmentsConfig.get(); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index 72df145b1c..da5f7617e6 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -292,7 +292,7 @@ private DistributionSetAssignmentResult assignDistributionSetToTargets(final Lon targetEntities.stream().map(Target::getId).collect(Collectors.toList()), Constants.MAX_ENTRIES_IN_STATEMENT); - closeOrCancelActiveActions(assignmentStrategy, targetEntitiesIdsChunks); + final Set cancellingTargetIds = closeOrCancelActiveActions(assignmentStrategy, targetEntitiesIdsChunks); // cancel all scheduled actions which are in-active, these actions were // not active before and the manual assignment which has been done // cancels them @@ -314,7 +314,10 @@ private DistributionSetAssignmentResult assignDistributionSetToTargets(final Lon return new DistributionSetAssignmentResult(distributionSetEntity, targetEntities.stream().map(Target::getControllerId).collect(Collectors.toList()), targetEntities.size(), controllerIDs.size() - targetEntities.size(), - Lists.newArrayList(controllerIdsToActions.values()), targetManagement); + controllerIdsToActions.values().stream() + .filter(action -> !cancellingTargetIds.contains(action.getTarget().getId())) + .collect(Collectors.toList()), + targetManagement); } private JpaDistributionSet getAndValidateDsById(final Long dsID) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java index 65af94a680..5a89d2d685 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java @@ -169,8 +169,7 @@ private DistributionSetAssignmentResult sendDistributionSetAssignedEvent( final DistributionSetAssignmentResult assignmentResult) { final List filteredActions = assignmentResult.getActions().stream().filter(action -> { final Status actionStatus = action.getStatus(); - return !hasPendingCancellations(action.getTarget()) && Status.CANCELING != actionStatus - && Status.CANCELED != actionStatus; + return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; }).collect(Collectors.toList()); final DistributionSet set = assignmentResult.getDistributionSet(); sendTargetAssignDistributionSetEvent(set.getTenant(), set.getId(), filteredActions); From 661e509700657a9033c581ef8d16547316478a38 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Fri, 3 May 2019 13:22:10 +0200 Subject: [PATCH 25/56] Revert latest perf fix (causing a regression) Signed-off-by: Stefan Behl --- .../repository/jpa/AbstractDsAssignmentStrategy.java | 5 +++++ .../hawkbit/repository/jpa/JpaDeploymentManagement.java | 7 ++----- .../hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java index 57331fcf99..c3fe7d61a2 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java @@ -242,6 +242,11 @@ JpaActionStatus createActionStatus(final JpaAction action, final String actionMe return actionStatus; } + boolean hasPendingCancellations(final Target target) { + return actionRepository.findByActiveAndTarget(null, target.getControllerId(), true).getContent().stream() + .anyMatch(action -> action.getStatus() == Status.CANCELING); + } + boolean isMultiAssignmentsEnabled() { return multiAssignmentsConfig.get(); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index da5f7617e6..72df145b1c 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -292,7 +292,7 @@ private DistributionSetAssignmentResult assignDistributionSetToTargets(final Lon targetEntities.stream().map(Target::getId).collect(Collectors.toList()), Constants.MAX_ENTRIES_IN_STATEMENT); - final Set cancellingTargetIds = closeOrCancelActiveActions(assignmentStrategy, targetEntitiesIdsChunks); + closeOrCancelActiveActions(assignmentStrategy, targetEntitiesIdsChunks); // cancel all scheduled actions which are in-active, these actions were // not active before and the manual assignment which has been done // cancels them @@ -314,10 +314,7 @@ private DistributionSetAssignmentResult assignDistributionSetToTargets(final Lon return new DistributionSetAssignmentResult(distributionSetEntity, targetEntities.stream().map(Target::getControllerId).collect(Collectors.toList()), targetEntities.size(), controllerIDs.size() - targetEntities.size(), - controllerIdsToActions.values().stream() - .filter(action -> !cancellingTargetIds.contains(action.getTarget().getId())) - .collect(Collectors.toList()), - targetManagement); + Lists.newArrayList(controllerIdsToActions.values()), targetManagement); } private JpaDistributionSet getAndValidateDsById(final Long dsID) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java index 5a89d2d685..65af94a680 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java @@ -169,7 +169,8 @@ private DistributionSetAssignmentResult sendDistributionSetAssignedEvent( final DistributionSetAssignmentResult assignmentResult) { final List filteredActions = assignmentResult.getActions().stream().filter(action -> { final Status actionStatus = action.getStatus(); - return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; + return !hasPendingCancellations(action.getTarget()) && Status.CANCELING != actionStatus + && Status.CANCELED != actionStatus; }).collect(Collectors.toList()); final DistributionSet set = assignmentResult.getDistributionSet(); sendTargetAssignDistributionSetEvent(set.getTenant(), set.getId(), filteredActions); From bec7599496648d38e9dd0d98c7832737c93cc56a Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Fri, 3 May 2019 13:27:01 +0200 Subject: [PATCH 26/56] test if a rollout sends multiaction messages in multiassignment mode Signed-off-by: Stefan Klotz --- .../amqp/AmqpMessageDispatcherService.java | 2 +- .../AbstractAmqpServiceIntegrationTest.java | 42 +++++++++++++-- ...ssageDispatcherServiceIntegrationTest.java | 52 ++++++++++++++++--- .../dmf/json/model/DmfMultiActionRequest.java | 6 +++ .../jpa/DeploymentManagementTest.java | 33 ++++++------ 5 files changed, 105 insertions(+), 30 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java index c6835c5daf..1b2eafcbdc 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java @@ -198,7 +198,7 @@ protected void sendMultiActionRequestToTarget(final String tenant, final Target final Map>> softwareModulesPerDistributionSet) { final URI targetAdress = target.getAddress(); - if (!IpUtil.isAmqpUri(targetAdress) || actions == null || actions.isEmpty()) { + if (!IpUtil.isAmqpUri(targetAdress) || CollectionUtils.isEmpty(actions)) { return; } diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java index da3f034801..c587fdcba5 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java @@ -35,6 +35,7 @@ import org.eclipse.hawkbit.dmf.json.model.DmfMetadata; 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.integration.listener.DeadletterListener; import org.eclipse.hawkbit.integration.listener.ReplyToListener; import org.eclipse.hawkbit.matcher.SoftwareModuleJsonMatcher; @@ -224,16 +225,49 @@ protected void assertDownloadAndInstallMessage(final Set softwar } + protected void assertLatestMultiActionMessageContainsInstallMessages(final String controllerId, + final List> 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> smIdsOfActionsFound = getDownloadAndUpdateRequests(multiActionRequest).stream() + .map(AbstractAmqpServiceIntegrationTest::getSmIds).collect(Collectors.toList()); + assertThat(smIdsOfActionsFound).containsExactlyElementsOf(smIdsOfActionsExpected); + } + + private static Set getSmIds(final DmfDownloadAndUpdateRequest request) { + return request.getSoftwareModules().stream().map(DmfSoftwareModule::getModuleId).collect(Collectors.toSet()); + } + + private static List getDownloadAndUpdateRequests( + final DmfMultiActionRequest request) { + return request.getElements().stream() + .filter(AbstractAmqpServiceIntegrationTest::isDownloadAndUpdateRequest) + .map(multiAction -> (DmfDownloadAndUpdateRequest) multiAction.getAction()).collect(Collectors.toList()); + } + + private static List getNonDownloadAndUpdateRequests( + final DmfMultiActionRequest request) { + return request.getElements().stream() + .filter(multiaction -> !isDownloadAndUpdateRequest(multiaction)).collect(Collectors.toList()); + } + + private static boolean isDownloadAndUpdateRequest(final DmfMultiActionElement multiActionElement) { + return multiActionElement.getTopic().equals(EventTopic.DOWNLOAD) + || multiActionElement.getTopic().equals(EventTopic.DOWNLOAD_AND_INSTALL); + } + protected void assertLatestMultiActionMessage(final String controllerId, final List> actionsExpected) { final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION); assertThat(multiactionMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID)) .isEqualTo(controllerId); - final DmfMultiActionRequest dmfMultiActionRequest = (DmfMultiActionRequest) getDmfClient().getMessageConverter() - .fromMessage(multiactionMessage); - - final List multiActionRequest = dmfMultiActionRequest.getElements(); + final List multiActionRequest = ((DmfMultiActionRequest) getDmfClient() + .getMessageConverter().fromMessage(multiactionMessage)).getElements(); final List> actionsFromMessage = multiActionRequest.stream() .map(request -> new SimpleEntry<>(request.getAction().getActionId(), request.getTopic())) .collect(Collectors.toList()); diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java index 8280596486..77167c2f13 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java @@ -17,8 +17,10 @@ import java.util.Arrays; import java.util.Map; 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.json.model.DmfActionStatus; @@ -38,6 +40,8 @@ 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; @@ -180,7 +184,7 @@ public void assignMultipleDsInMultiAssignMode() { @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 = 2) }) + @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) }) public void cancelActionInMultiAssignMode() { setMultiAssignmentsEnabled(true); final String controllerId = TARGET_PREFIX + "cancelActionInMultiAssignMode"; @@ -199,7 +203,7 @@ public void cancelActionInMultiAssignMode() { assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Cancel, action2Install)); updateActionViaDmfClient(controllerId, actionId1, DmfActionStatus.CANCELED); - createAndSendThingCreated(controllerId, TENANT_EXIST); + waitUntilEventMessagesAreSent(4); assertLatestMultiActionMessage(controllerId, Arrays.asList(action2Install)); } @@ -214,7 +218,7 @@ public void cancelActionInMultiAssignMode() { @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 = 2) }) + @Expect(type = TargetUpdatedEvent.class, count = 3), @Expect(type = TargetPollEvent.class, count = 1) }) public void finishActionInMultiAssignMode() { setMultiAssignmentsEnabled(true); final String controllerId = TARGET_PREFIX + "finishActionInMultiAssignMode"; @@ -225,11 +229,8 @@ public void finishActionInMultiAssignMode() { waitUntilEventMessagesAreSent(2); updateActionViaDmfClient(controllerId, actionId1, DmfActionStatus.FINISHED); - waitUntilEventMessagesAreSent(3); - assertRequestAttributesUpdateMessage(controllerId); - - createAndSendThingCreated(controllerId, TENANT_EXIST); waitUntilEventMessagesAreSent(4); + assertRequestAttributesUpdateMessage(controllerId); assertLatestMultiActionMessage(controllerId, actionId2, EventTopic.DOWNLOAD_AND_INSTALL); } @@ -271,6 +272,43 @@ private Long assignNewDsToTarget(final String controllerId) { return assignDistributionSet(ds.getId(), controllerId).getActionIds().get(0); } + @Test + @Description("If multi assignment is enabled a running rollout sends multi-action messages.") + @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), + @Expect(type = DeploymentEvent.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 startRolloutInMultiAssignMode() { + setMultiAssignmentsEnabled(true); + final String controllerId = TARGET_PREFIX + "assignDsMultipleTimesInMultiAssignMode"; + + registerAndAssertTargetWithExistingTenant(controllerId); + final DistributionSet ds1 = testdataFactory.createDistributionSet(UUID.randomUUID().toString()); + final Set smIds1 = ds1.getModules().stream().map(SoftwareModule::getId).collect(Collectors.toSet()); + final String filterQuery = "controllerId==" + controllerId; + final Rollout rollout1 = testdataFactory.createRolloutByVariables("multiassignRollout1", "", 1, filterQuery, + ds1, + "50", "5"); + final Rollout rollout2 = testdataFactory.createRolloutByVariables("multiassignRollout2", "", 1, filterQuery, + ds1, + "50", "5"); + + rolloutManagement.start(rollout1.getId()); + rolloutManagement.handleRollouts(); + waitUntilEventMessagesAreSent(1); + assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds1)); + + rolloutManagement.start(rollout2.getId()); + rolloutManagement.handleRollouts(); + waitUntilEventMessagesAreSent(2); + assertLatestMultiActionMessageContainsInstallMessages(controllerId, + Arrays.asList(smIds1, smIds1)); + } + @Test @Description("Verify that a cancel assignment send a cancel message.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), diff --git a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java index 20498b74b1..0d6bd36a21 100644 --- a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java +++ b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java @@ -18,6 +18,9 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonSubTypes.Type; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonValue; /** @@ -68,6 +71,9 @@ public DmfActionRequest getAction() { return action; } + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "topic", defaultImpl = DmfActionRequest.class) + @JsonSubTypes({ @Type(value = DmfDownloadAndUpdateRequest.class, name = "DOWNLOAD"), + @Type(value = DmfDownloadAndUpdateRequest.class, name = "DOWNLOAD_AND_INSTALL") }) public void setAction(final DmfActionRequest action) { this.action = action; } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java index b5a9f6e4b3..bb588629c2 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java @@ -25,6 +25,7 @@ import java.util.stream.IntStream; import org.eclipse.hawkbit.repository.ActionStatusFields; +import org.eclipse.hawkbit.repository.event.remote.DeploymentEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; @@ -539,28 +540,24 @@ public void assignDistributionSetAndAutoCloseActiveActions() { @Expect(type = TargetUpdatedEvent.class, count = 20), @Expect(type = ActionCreatedEvent.class, count = 20), @Expect(type = DistributionSetCreatedEvent.class, count = 2), @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 2) }) + @Expect(type = DeploymentEvent.class, count = 2), + @Expect(type = TargetAssignDistributionSetEvent.class, count = 0) }) public void previousAssignmentsAreNotCanceledInMultiAssignMode() { setMultiAssignmentsEnabled(true); - try { - final List targets = testdataFactory.createTargets(10); - - // First assignment - final DistributionSet ds1 = testdataFactory.createDistributionSet("Multi-assign-1"); - assignDistributionSet(ds1, targets); + final List targets = testdataFactory.createTargets(10); - assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), true, Status.RUNNING); + // First assignment + final DistributionSet ds1 = testdataFactory.createDistributionSet("Multi-assign-1"); + assignDistributionSet(ds1, targets); - // Second assignment - final DistributionSet ds2 = testdataFactory.createDistributionSet("Multi-assign-2"); - assignDistributionSet(ds2, targets); + assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), true, Status.RUNNING); - assertDsExclusivelyAssignedToTargets(targets, ds2.getId(), true, Status.RUNNING); - assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), true, Status.RUNNING); + // Second assignment + final DistributionSet ds2 = testdataFactory.createDistributionSet("Multi-assign-2"); + assignDistributionSet(ds2, targets); - } finally { - setMultiAssignmentsEnabled(false); - } + assertDsExclusivelyAssignedToTargets(targets, ds2.getId(), true, Status.RUNNING); + assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), true, Status.RUNNING); } private void assertDsExclusivelyAssignedToTargets(final List targets, final long dsId, final boolean active, @@ -1117,10 +1114,10 @@ private void assertTargetAssignDistributionSetEvents(final List targets, assertThat(event).isNotNull(); assertThat(event.getDistributionSetId()).isEqualTo(ds.getId()); - List eventActionIds = event.getActions().values().stream().map(ActionProperties::getId) + final List eventActionIds = event.getActions().values().stream().map(ActionProperties::getId) .collect(Collectors.toList()); - List targetActiveActionIds = targets.stream() + final List targetActiveActionIds = targets.stream() .map(t -> deploymentManagement.findActiveActionsByTarget(PAGE, t.getControllerId()).getContent()) .flatMap(List::stream) .map(Action::getId) From d93b1373de2c2bbfba023544d7a4741964c7e859 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Fri, 3 May 2019 13:45:08 +0200 Subject: [PATCH 27/56] Minor changes Signed-off-by: Stefan Behl --- .../AbstractAmqpServiceIntegrationTest.java | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java index c587fdcba5..a5dd6bcab7 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java @@ -112,12 +112,12 @@ protected T waitUntilIsPresent(final Callable> callable) { } protected void waitUntilEventMessagesAreSent(final int numberOfMessages) { - createConditionFactory().until(() -> { + createConditionFactory().untilAsserted(() -> { int messagesReceived = 0; for (final List messages : replyToListener.getEventMessages().values()) { messagesReceived += messages.size(); } - return messagesReceived == numberOfMessages; + assertThat(messagesReceived).isEqualTo(numberOfMessages); }); } @@ -242,17 +242,14 @@ private static Set getSmIds(final DmfDownloadAndUpdateRequest request) { return request.getSoftwareModules().stream().map(DmfSoftwareModule::getModuleId).collect(Collectors.toSet()); } - private static List getDownloadAndUpdateRequests( - final DmfMultiActionRequest request) { - return request.getElements().stream() - .filter(AbstractAmqpServiceIntegrationTest::isDownloadAndUpdateRequest) + private static List getDownloadAndUpdateRequests(final DmfMultiActionRequest request) { + return request.getElements().stream().filter(AbstractAmqpServiceIntegrationTest::isDownloadAndUpdateRequest) .map(multiAction -> (DmfDownloadAndUpdateRequest) multiAction.getAction()).collect(Collectors.toList()); } - private static List getNonDownloadAndUpdateRequests( - final DmfMultiActionRequest request) { - return request.getElements().stream() - .filter(multiaction -> !isDownloadAndUpdateRequest(multiaction)).collect(Collectors.toList()); + private static List getNonDownloadAndUpdateRequests(final DmfMultiActionRequest request) { + return request.getElements().stream().filter(multiaction -> !isDownloadAndUpdateRequest(multiaction)) + .collect(Collectors.toList()); } private static boolean isDownloadAndUpdateRequest(final DmfMultiActionElement multiActionElement) { @@ -265,7 +262,7 @@ protected void assertLatestMultiActionMessage(final String controllerId, final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION); assertThat(multiactionMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID)) .isEqualTo(controllerId); - + final List multiActionRequest = ((DmfMultiActionRequest) getDmfClient() .getMessageConverter().fromMessage(multiactionMessage)).getElements(); final List> actionsFromMessage = multiActionRequest.stream() From e8c83148605ff3abf28a611bcefea94992b73d36 Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Fri, 3 May 2019 16:36:23 +0200 Subject: [PATCH 28/56] add test that starts and finishes multiple rollouts in multiassignt mode Signed-off-by: Stefan Klotz --- .../AbstractAmqpServiceIntegrationTest.java | 21 ++- ...ssageDispatcherServiceIntegrationTest.java | 123 +++++++++++++----- .../integration/listener/ReplyToListener.java | 1 - 3 files changed, 99 insertions(+), 46 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java index a5dd6bcab7..c451f967f4 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java @@ -12,7 +12,6 @@ import java.nio.charset.StandardCharsets; import java.util.AbstractMap.SimpleEntry; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -258,23 +257,21 @@ private static boolean isDownloadAndUpdateRequest(final DmfMultiActionElement mu } protected void assertLatestMultiActionMessage(final String controllerId, - final List> actionsExpected) { + final List> actionsExpected) { + final List> actionsFromMessage = getLatestLatestMultiActionMessageActions(controllerId); + assertThat(actionsFromMessage).containsExactlyElementsOf(actionsExpected); + } + + protected List> getLatestLatestMultiActionMessageActions( + final String expectedControllerId) { final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION); assertThat(multiactionMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID)) - .isEqualTo(controllerId); - + .isEqualTo(expectedControllerId); final List multiActionRequest = ((DmfMultiActionRequest) getDmfClient() .getMessageConverter().fromMessage(multiactionMessage)).getElements(); - final List> actionsFromMessage = multiActionRequest.stream() + return multiActionRequest.stream() .map(request -> new SimpleEntry<>(request.getAction().getActionId(), request.getTopic())) .collect(Collectors.toList()); - assertThat(actionsFromMessage).containsExactlyElementsOf(actionsExpected); - } - - protected void assertLatestMultiActionMessage(final String controllerId, final long actionId, - final EventTopic topic) { - final SimpleEntry action = new SimpleEntry<>(actionId, topic); - assertLatestMultiActionMessage(controllerId, Collections.singletonList(action)); } protected void assertDownloadMessage(final Set dsModules, final String controllerId) { diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java index 77167c2f13..3e268951eb 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java @@ -15,7 +15,9 @@ 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; @@ -33,6 +35,10 @@ 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; @@ -163,14 +169,12 @@ public void assignMultipleDsInMultiAssignMode() { registerAndAssertTargetWithExistingTenant(controllerId); final Long actionId1 = assignNewDsToTarget(controllerId); - final SimpleEntry action1Install = new SimpleEntry(actionId1, - EventTopic.DOWNLOAD_AND_INSTALL); + final Entry action1Install = new SimpleEntry<>(actionId1, EventTopic.DOWNLOAD_AND_INSTALL); waitUntilEventMessagesAreSent(1); assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Install)); final Long actionId2 = assignNewDsToTarget(controllerId); - final SimpleEntry action2Install = new SimpleEntry(actionId2, - EventTopic.DOWNLOAD_AND_INSTALL); + final Entry action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL); waitUntilEventMessagesAreSent(2); assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Install, action2Install)); } @@ -196,10 +200,8 @@ public void cancelActionInMultiAssignMode() { deploymentManagement.cancelAction(actionId1); waitUntilEventMessagesAreSent(3); - final SimpleEntry action1Cancel = new SimpleEntry(actionId1, - EventTopic.CANCEL_DOWNLOAD); - final SimpleEntry action2Install = new SimpleEntry(actionId2, - EventTopic.DOWNLOAD_AND_INSTALL); + final Entry action1Cancel = new SimpleEntry<>(actionId1, EventTopic.CANCEL_DOWNLOAD); + final Entry action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL); assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Cancel, action2Install)); updateActionViaDmfClient(controllerId, actionId1, DmfActionStatus.CANCELED); @@ -226,12 +228,13 @@ public void finishActionInMultiAssignMode() { final long actionId1 = assignNewDsToTarget(controllerId); final long actionId2 = assignNewDsToTarget(controllerId); + final Entry action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL); waitUntilEventMessagesAreSent(2); updateActionViaDmfClient(controllerId, actionId1, DmfActionStatus.FINISHED); waitUntilEventMessagesAreSent(4); assertRequestAttributesUpdateMessage(controllerId); - assertLatestMultiActionMessage(controllerId, actionId2, EventTopic.DOWNLOAD_AND_INSTALL); + assertLatestMultiActionMessage(controllerId, Arrays.asList(action2Install)); } @Test @@ -255,10 +258,8 @@ public void assignDsMultipleTimesInMultiAssignMode() { final Long actionId2 = assignDistributionSet(ds.getId(), controllerId).getActionIds().get(0); waitUntilEventMessagesAreSent(2); - final SimpleEntry action1Install = new SimpleEntry<>(actionId1, - EventTopic.DOWNLOAD_AND_INSTALL); - final SimpleEntry action2Install = new SimpleEntry<>(actionId2, - EventTopic.DOWNLOAD_AND_INSTALL); + final Entry action1Install = new SimpleEntry<>(actionId1, EventTopic.DOWNLOAD_AND_INSTALL); + final Entry action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL); assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Install, action2Install)); } @@ -272,41 +273,97 @@ private Long assignNewDsToTarget(final String controllerId) { return assignDistributionSet(ds.getId(), controllerId).getActionIds().get(0); } + // TODO: check rollout event counts @Test - @Description("If multi assignment is enabled a running rollout sends multi-action messages.") + @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 = DeploymentEvent.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 = 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 = 2), @Expect(type = TargetPollEvent.class, count = 1) }) - public void startRolloutInMultiAssignMode() { + @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() { setMultiAssignmentsEnabled(true); - final String controllerId = TARGET_PREFIX + "assignDsMultipleTimesInMultiAssignMode"; + final String controllerId = TARGET_PREFIX + "startRolloutsWithSameDsInMultiAssignMode"; registerAndAssertTargetWithExistingTenant(controllerId); - final DistributionSet ds1 = testdataFactory.createDistributionSet(UUID.randomUUID().toString()); - final Set smIds1 = ds1.getModules().stream().map(SoftwareModule::getId).collect(Collectors.toSet()); + final DistributionSet ds = testdataFactory.createDistributionSet(UUID.randomUUID().toString()); + final Set smIds = getSoftwareModuleIds(ds); final String filterQuery = "controllerId==" + controllerId; - final Rollout rollout1 = testdataFactory.createRolloutByVariables("multiassignRollout1", "", 1, filterQuery, - ds1, - "50", "5"); - final Rollout rollout2 = testdataFactory.createRolloutByVariables("multiassignRollout2", "", 1, filterQuery, - ds1, - "50", "5"); - - rolloutManagement.start(rollout1.getId()); - rolloutManagement.handleRollouts(); + + startRollout(ds, filterQuery); waitUntilEventMessagesAreSent(1); + assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds)); + + startRollout(ds, filterQuery); + waitUntilEventMessagesAreSent(2); + assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds, smIds)); + } + + // TODO: check rollout event counts + @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 = DeploymentEvent.class, count = 3), + @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), + @Expect(type = CancelTargetAssignmentEvent.class, count = 0), + @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 stratMultipleRolloutsAndFinishInMultiAssignMode() { + setMultiAssignmentsEnabled(true); + final String controllerId = TARGET_PREFIX + "stratMultipleRolloutsAndFinishInMultiAssignMode"; + + registerAndAssertTargetWithExistingTenant(controllerId); + final String filterQuery = "controllerId==" + controllerId; + final DistributionSet ds1 = testdataFactory.createDistributionSet(UUID.randomUUID().toString()); + final Set smIds1 = getSoftwareModuleIds(ds1); + final DistributionSet ds2 = testdataFactory.createDistributionSet(UUID.randomUUID().toString()); + final Set smIds2 = getSoftwareModuleIds(ds2); + + startRollout(ds1, filterQuery); + startRollout(ds2, filterQuery); + waitUntilEventMessagesAreSent(2); + startRollout(ds1, filterQuery); + waitUntilEventMessagesAreSent(3); + assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds1, smIds2, smIds1)); + + final List installActions = getLatestLatestMultiActionMessageActions(controllerId).stream() + .filter(entry -> entry.getValue().equals(EventTopic.DOWNLOAD_AND_INSTALL)).map(Entry::getKey) + .collect(Collectors.toList()); + + updateActionViaDmfClient(controllerId, installActions.get(0), DmfActionStatus.FINISHED); + // sends REQUEST_ATTRIBUTES_UPDATE + waitUntilEventMessagesAreSent(5); + assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds2, smIds1)); + + updateActionViaDmfClient(controllerId, installActions.get(1), DmfActionStatus.FINISHED); + // sends REQUEST_ATTRIBUTES_UPDATE + waitUntilEventMessagesAreSent(7); assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds1)); + } + + private Set getSoftwareModuleIds(final DistributionSet ds) { + return ds.getModules().stream().map(SoftwareModule::getId).collect(Collectors.toSet()); + } - rolloutManagement.start(rollout2.getId()); + private Rollout startRollout(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(); - waitUntilEventMessagesAreSent(2); - assertLatestMultiActionMessageContainsInstallMessages(controllerId, - Arrays.asList(smIds1, smIds1)); + return rollout; } @Test diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java index 2af41c7f46..b3672483e5 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java @@ -10,7 +10,6 @@ import static org.junit.Assert.fail; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.EnumMap; From 509011e72951de3439c3c6784bdb8e82ee165cd8 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Fri, 3 May 2019 14:23:09 +0200 Subject: [PATCH 29/56] Do not close new action if DS is already assigned (if multi-assign on) Signed-off-by: Stefan Behl --- .../hawkbit/repository/jpa/JpaDeploymentManagement.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index 72df145b1c..676f3dd473 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -534,6 +534,11 @@ private Page findActionsByRolloutAndRolloutGroupParent(final Long rollou } private JpaAction closeActionIfSetWasAlreadyAssigned(final JpaAction action) { + + if (isMultiAssignmentsEnabled()) { + return action; + } + final JpaTarget target = (JpaTarget) action.getTarget(); if (target.getAssignedDistributionSet() != null && action.getDistributionSet().getId().equals(target.getAssignedDistributionSet().getId())) { From e3a18c518ff9634c6c4b29821350992b39156d10 Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Mon, 6 May 2019 09:41:23 +0200 Subject: [PATCH 30/56] add javadoc to test method Signed-off-by: Stefan Klotz --- .../integration/AbstractAmqpServiceIntegrationTest.java | 9 +++++++++ .../AmqpMessageDispatcherServiceIntegrationTest.java | 2 -- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java index c451f967f4..a6e2ad184f 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java @@ -110,6 +110,15 @@ protected T waitUntilIsPresent(final Callable> callable) { } } + /** + * Checks the total number of {@link Message} with type + * {@link MessageType#EVENT} that has been received by the + * {@link ReplyToListener} since the beginning of the test. Waits for the + * exact number, times out when there are more messages than expected. + * + * @param numberOfMessages + * exact number of expected messages + */ protected void waitUntilEventMessagesAreSent(final int numberOfMessages) { createConditionFactory().untilAsserted(() -> { int messagesReceived = 0; diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java index 3e268951eb..2933e6eebe 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java @@ -310,8 +310,6 @@ public void startRolloutsWithSameDsInMultiAssignMode() { @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 = DeploymentEvent.class, count = 3), - @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), - @Expect(type = CancelTargetAssignmentEvent.class, count = 0), @Expect(type = ActionCreatedEvent.class, count = 3), @Expect(type = ActionUpdatedEvent.class, count = 5), @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), @Expect(type = DistributionSetCreatedEvent.class, count = 2), From d02b8cd0a8eb8fcc7f9439e700616668ce7c81d5 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Tue, 7 May 2019 14:49:42 +0200 Subject: [PATCH 31/56] Prevent Multi-Assignments from being disabled via Repo Config UI Signed-off-by: Stefan Behl --- .../ui/tenantconfiguration/RepositoryConfigurationView.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RepositoryConfigurationView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RepositoryConfigurationView.java index fb3d3bf81a..d287c7397e 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RepositoryConfigurationView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RepositoryConfigurationView.java @@ -99,6 +99,8 @@ private void init() { multiAssignmentsCheckBox.setId(UIComponentIdProvider.REPOSITORY_MULTI_ASSIGNMENTS_CHECKBOX); multiAssignmentsCheckBox.setValue(multiAssignmentsConfigurationItem.isConfigEnabled()); multiAssignmentsCheckBox.addValueChangeListener(this); + multiAssignmentsCheckBox.setEnabled(!isMultiAssignmentsEnabled); + multiAssignmentsConfigurationItem.setEnabled(!isMultiAssignmentsEnabled); multiAssignmentsConfigurationItem.addChangeListener(this); gridLayout.addComponent(multiAssignmentsCheckBox, 0, 1); gridLayout.addComponent(multiAssignmentsConfigurationItem, 1, 1); @@ -121,6 +123,10 @@ public void save() { actionAutocloseConfigurationItem.save(); actionAutocleanupConfigurationItem.save(); multiAssignmentsConfigurationItem.save(); + + final boolean isMultiAssignmentsEnabled = multiAssignmentsConfigurationItem.isConfigEnabled(); + multiAssignmentsCheckBox.setEnabled(!isMultiAssignmentsEnabled); + multiAssignmentsConfigurationItem.setEnabled(!isMultiAssignmentsEnabled); } @Override From 15d64b219ee2277914a203418669399192079c0e Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Tue, 7 May 2019 15:29:24 +0200 Subject: [PATCH 32/56] Add link to Provisioning State Machine Signed-off-by: Stefan Behl --- .../org/eclipse/hawkbit/ui/UiProperties.java | 10 ++++++++++ .../RepositoryConfigurationView.java | 15 ++++++++++++-- .../RolloutConfigurationView.java | 20 ++++++++++--------- .../TenantConfigurationDashboardView.java | 3 ++- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UiProperties.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UiProperties.java index cb1c11dec4..90a0de743c 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UiProperties.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UiProperties.java @@ -209,6 +209,8 @@ public static class Documentation implements Serializable { */ private String rolloutView = ""; + private String provisioningStateMachine = ""; + public String getDeploymentView() { return deploymentView; } @@ -249,6 +251,10 @@ public String getMaintenanceWindowView() { return maintenanceWindowView; } + public String getProvisioningStateMachine() { + return provisioningStateMachine; + } + public void setDeploymentView(final String deploymentView) { this.deploymentView = deploymentView; } @@ -289,6 +295,10 @@ public void setMaintenanceWindowView(final String maintenanceWindowView) { this.maintenanceWindowView = maintenanceWindowView; } + public void setProvisioningStateMachine(final String provisioningStateMachine) { + this.provisioningStateMachine = provisioningStateMachine; + } + } private final Documentation documentation = new Documentation(); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RepositoryConfigurationView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RepositoryConfigurationView.java index d287c7397e..7549fb7835 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RepositoryConfigurationView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RepositoryConfigurationView.java @@ -9,6 +9,7 @@ package org.eclipse.hawkbit.ui.tenantconfiguration; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; +import org.eclipse.hawkbit.ui.UiProperties; import org.eclipse.hawkbit.ui.components.SPUIComponentProvider; import org.eclipse.hawkbit.ui.tenantconfiguration.generic.BooleanConfigurationItem; import org.eclipse.hawkbit.ui.tenantconfiguration.repository.ActionAutocleanupConfigurationItem; @@ -19,9 +20,11 @@ import com.vaadin.data.Property.ValueChangeEvent; import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.ui.Alignment; import com.vaadin.ui.CheckBox; import com.vaadin.ui.GridLayout; import com.vaadin.ui.Label; +import com.vaadin.ui.Link; import com.vaadin.ui.Panel; import com.vaadin.ui.VerticalLayout; @@ -37,6 +40,8 @@ public class RepositoryConfigurationView extends BaseConfigurationView private final VaadinMessageSource i18n; + private final UiProperties uiProperties; + private final ActionAutocloseConfigurationItem actionAutocloseConfigurationItem; private final ActionAutocleanupConfigurationItem actionAutocleanupConfigurationItem; @@ -50,8 +55,9 @@ public class RepositoryConfigurationView extends BaseConfigurationView private CheckBox multiAssignmentsCheckBox; RepositoryConfigurationView(final VaadinMessageSource i18n, - final TenantConfigurationManagement tenantConfigurationManagement) { + final TenantConfigurationManagement tenantConfigurationManagement, final UiProperties uiProperties) { this.i18n = i18n; + this.uiProperties = uiProperties; this.actionAutocloseConfigurationItem = new ActionAutocloseConfigurationItem(tenantConfigurationManagement, i18n); this.actionAutocleanupConfigurationItem = new ActionAutocleanupConfigurationItem(tenantConfigurationManagement, @@ -77,7 +83,7 @@ private void init() { header.addStyleName("config-panel-header"); vLayout.addComponent(header); - final GridLayout gridLayout = new GridLayout(2, 3); + final GridLayout gridLayout = new GridLayout(3, 3); gridLayout.setSpacing(true); gridLayout.setImmediate(true); gridLayout.setColumnExpandRatio(1, 1.0F); @@ -113,6 +119,11 @@ private void init() { gridLayout.addComponent(actionAutocleanupCheckBox, 0, 2); gridLayout.addComponent(actionAutocleanupConfigurationItem, 1, 2); + final Link linkToProvisioningHelp = SPUIComponentProvider.getHelpLink(i18n, + uiProperties.getLinks().getDocumentation().getProvisioningStateMachine()); + gridLayout.addComponent(linkToProvisioningHelp, 2, 2); + gridLayout.setComponentAlignment(linkToProvisioningHelp, Alignment.BOTTOM_RIGHT); + vLayout.addComponent(gridLayout); rootPanel.setContent(vLayout); setCompositionRoot(rootPanel); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RolloutConfigurationView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RolloutConfigurationView.java index 85e6d1eb5f..3352b6a49d 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RolloutConfigurationView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/RolloutConfigurationView.java @@ -18,7 +18,7 @@ import com.vaadin.data.Property; import com.vaadin.ui.Alignment; import com.vaadin.ui.CheckBox; -import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.GridLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Link; import com.vaadin.ui.Panel; @@ -61,24 +61,26 @@ private void init() { header.addStyleName("config-panel-header"); vLayout.addComponent(header); - final HorizontalLayout hLayout = new HorizontalLayout(); - hLayout.setSpacing(true); - hLayout.setImmediate(true); + final GridLayout gridLayout = new GridLayout(3, 1); + gridLayout.setSpacing(true); + gridLayout.setImmediate(true); + gridLayout.setColumnExpandRatio(1, 1.0F); + gridLayout.setSizeFull(); approvalCheckbox = SPUIComponentProvider.getCheckBox("", "", null, false, ""); approvalCheckbox.setId(UIComponentIdProvider.ROLLOUT_APPROVAL_ENABLED_CHECKBOX); approvalCheckbox.setValue(approvalConfigurationItem.isConfigEnabled()); approvalCheckbox.addValueChangeListener(this); approvalConfigurationItem.addChangeListener(this); - hLayout.addComponent(approvalCheckbox); - hLayout.addComponent(approvalConfigurationItem); + gridLayout.addComponent(approvalCheckbox, 0, 0); + gridLayout.addComponent(approvalConfigurationItem, 1, 0); final Link linkToApprovalHelp = SPUIComponentProvider.getHelpLink(i18n, uiProperties.getLinks().getDocumentation().getRollout()); - hLayout.addComponent(linkToApprovalHelp); - hLayout.setComponentAlignment(linkToApprovalHelp, Alignment.BOTTOM_RIGHT); + gridLayout.addComponent(linkToApprovalHelp, 2, 0); + gridLayout.setComponentAlignment(linkToApprovalHelp, Alignment.BOTTOM_RIGHT); - vLayout.addComponent(hLayout); + vLayout.addComponent(gridLayout); rootPanel.setContent(vLayout); setCompositionRoot(rootPanel); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/TenantConfigurationDashboardView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/TenantConfigurationDashboardView.java index a6477fe46d..dd2b596c2d 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/TenantConfigurationDashboardView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/TenantConfigurationDashboardView.java @@ -91,7 +91,8 @@ public class TenantConfigurationDashboardView extends CustomComponent implements securityTokenGenerator, uiProperties); this.pollingConfigurationView = new PollingConfigurationView(i18n, controllerPollProperties, tenantConfigurationManagement); - this.repositoryConfigurationView = new RepositoryConfigurationView(i18n, tenantConfigurationManagement); + this.repositoryConfigurationView = new RepositoryConfigurationView(i18n, tenantConfigurationManagement, + uiProperties); this.rolloutConfigurationView = new RolloutConfigurationView(i18n, tenantConfigurationManagement, uiProperties); this.i18n = i18n; From 95df24b304169b756e5c37ad96b7270a93396f6c Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Wed, 8 May 2019 09:41:38 +0200 Subject: [PATCH 33/56] test that Multiassignment can not be disabled via mgmt-api Signed-off-by: Stefan Klotz --- .../MgmtTenantManagementResourceTest.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java new file mode 100644 index 0000000000..ec2b1d1589 --- /dev/null +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2019 Bosch Software Innovations 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.mgmt.rest.resource; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants; +import org.eclipse.hawkbit.rest.util.MockMvcResultPrinter; +import org.json.JSONObject; +import org.junit.Test; +import org.springframework.http.MediaType; + +import io.qameta.allure.Description; +import io.qameta.allure.Feature; +import io.qameta.allure.Story; + +/** + * Spring MVC Tests against the MgmtTenantManagementResource. + * + */ +@Feature("Component Tests - Management API") +@Story("Tenant Management Resource") +public class MgmtTenantManagementResourceTest extends AbstractManagementApiIntegrationTest { + + @Test + @Description("Multiassignment can not be deactivated.") + public void deactivateMultiassignment() throws Exception { + final String multiassignmentKey = "multi.assignments.enabled"; + final String bodyActivate = new JSONObject().put("value", true).toString(); + final String bodyDeactivate = new JSONObject().put("value", false).toString(); + + mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", multiassignmentKey) + .content(bodyActivate).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) + .andExpect(status().isOk()); + + mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", multiassignmentKey) + .content(bodyDeactivate).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) + .andExpect(status().isForbidden()); + } +} From e88745b4ce5dc03b47693378da5d68906c0ebcf7 Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Wed, 8 May 2019 16:16:31 +0200 Subject: [PATCH 34/56] refactor AmqpMessageHandlerService code Signed-off-by: Stefan Klotz --- .../amqp/AmqpMessageHandlerService.java | 87 ++++++++----------- 1 file changed, 35 insertions(+), 52 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java index d1c272bc9a..bd6c548b2a 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java @@ -13,7 +13,6 @@ import java.io.Serializable; import java.net.URI; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -60,8 +59,6 @@ import org.springframework.security.core.context.SecurityContextImpl; import org.springframework.util.StringUtils; -import com.google.common.collect.Maps; - /** * * {@link AmqpMessageHandlerService} handles all incoming target interaction @@ -210,43 +207,31 @@ private void registerTarget(final Message message, final String virtualHost) { final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(thingId, amqpUri); LOG.debug("Target {} reported online state.", thingId); - checkIfUpdatesAvailable(target); + sendCurrentUpdateCommandToTarget(target); } - private void checkIfUpdatesAvailable(final Target target) { + private void sendCurrentUpdateCommandToTarget(final Target target) { if (isMultiAssignmentsEnabled()) { - final List actions = controllerManagement - .findActiveActionsByTarget(PageRequest.of(0, MAX_ACTIONS), target.getControllerId()).getContent(); - - final Set distributionSets = actions.stream().map(Action::getDistributionSet) - .collect(Collectors.toSet()); - final Map>> softwareModulesPerDistributionSet = distributionSets - .stream().collect(Collectors.toMap(DistributionSet::getId, this::getSoftwareModulesWithMetadata)); - - amqpMessageDispatcherService.sendMultiActionRequestToTarget(target.getTenant(), target, actions, - softwareModulesPerDistributionSet); + sendCurrentActionsAsMultiActionToTarget(target); } else { - checkIfUpdateAvailable(target); + sendOldestActionToTarget(target); } } - private Map> getSoftwareModulesWithMetadata( - final DistributionSet distributionSet) { - final Map softwareModules = distributionSet.getModules().stream() - .collect(Collectors.toMap(SoftwareModule::getId, sm -> sm)); - - final Map> metadataBySm = controllerManagement - .findTargetVisibleMetaDataBySoftwareModuleId(softwareModules.keySet()); - - final Map> softwareModulesWithMetadata = new HashMap<>(); - softwareModules.forEach((id, sm) -> { - final List metadata = metadataBySm.get(id); - softwareModulesWithMetadata.put(sm, metadata != null ? metadata : Collections.emptyList()); - }); - return softwareModulesWithMetadata; + private void sendCurrentActionsAsMultiActionToTarget(final Target target) { + final List actions = controllerManagement + .findActiveActionsByTarget(PageRequest.of(0, MAX_ACTIONS), target.getControllerId()).getContent(); + + final Set distributionSets = actions.stream().map(Action::getDistributionSet) + .collect(Collectors.toSet()); + final Map>> softwareModulesPerDistributionSet = distributionSets + .stream().collect(Collectors.toMap(DistributionSet::getId, this::getSoftwareModulesWithMetadata)); + + amqpMessageDispatcherService.sendMultiActionRequestToTarget(target.getTenant(), target, actions, + softwareModulesPerDistributionSet); } - private void checkIfUpdateAvailable(final Target target) { + private void sendOldestActionToTarget(final Target target) { final Optional actionOptional = controllerManagement .findOldestActiveActionByTarget(target.getControllerId()); @@ -258,20 +243,23 @@ private void checkIfUpdateAvailable(final Target target) { if (action.isCancelingOrCanceled()) { amqpMessageDispatcherService.sendCancelMessageToTarget(target.getTenant(), target.getControllerId(), action.getId(), target.getAddress()); - return; } + else { + amqpMessageDispatcherService.sendUpdateMessageToTarget(new ActionProperties(action), action.getTarget(), + getSoftwareModulesWithMetadata(action.getDistributionSet())); + } + } - final Map> modules = Maps - .newHashMapWithExpectedSize(action.getDistributionSet().getModules().size()); + private Map> getSoftwareModulesWithMetadata( + final DistributionSet distributionSet) { + final List smIds = distributionSet.getModules().stream().map(SoftwareModule::getId) + .collect(Collectors.toList()); final Map> metadata = controllerManagement - .findTargetVisibleMetaDataBySoftwareModuleId(action.getDistributionSet().getModules().stream() - .map(SoftwareModule::getId).collect(Collectors.toList())); + .findTargetVisibleMetaDataBySoftwareModuleId(smIds); - action.getDistributionSet().getModules().forEach(module -> modules.put(module, metadata.get(module.getId()))); - - amqpMessageDispatcherService.sendUpdateMessageToTarget(new ActionProperties(action), action.getTarget(), - modules); + return distributionSet.getModules().stream() + .collect(Collectors.toMap(sm -> sm, sm -> metadata.get(sm.getId()))); } /** @@ -326,11 +314,13 @@ private void updateActionStatus(final Message message) { final ActionStatusCreate actionStatus = entityFactory.actionStatus().create(action.getId()).status(status) .messages(messages); - final Action addUpdateActionStatus = getUpdateActionStatus(status, actionStatus); + final Action updatedAction = Status.CANCELED.equals(status) + ? controllerManagement.addCancelActionStatus(actionStatus) + : controllerManagement.addUpdateActionStatus(actionStatus); - if (!addUpdateActionStatus.isActive() || (addUpdateActionStatus.hasMaintenanceSchedule() - && addUpdateActionStatus.isMaintenanceWindowAvailable())) { - checkIfUpdatesAvailable(action.getTarget()); + if (!updatedAction.isActive() + || (updatedAction.hasMaintenanceSchedule() && updatedAction.isMaintenanceWindowAvailable())) { + sendCurrentUpdateCommandToTarget(action.getTarget()); } } @@ -388,13 +378,6 @@ private Status handleCancelRejectedState(final Message message, final Action act return null; } - private Action getUpdateActionStatus(final Status status, final ActionStatusCreate actionStatus) { - if (Status.CANCELED.equals(status)) { - return controllerManagement.addCancelActionStatus(actionStatus); - } - return controllerManagement.addUpdateActionStatus(actionStatus); - } - // Exception squid:S3655 - logAndThrowMessageError throws exception, i.e. // get will not be called @SuppressWarnings("squid:S3655") @@ -425,7 +408,7 @@ private static UpdateMode getUpdateMode(final DmfAttributeUpdate update) { } private boolean isMultiAssignmentsEnabled() { - return getConfigValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class, Boolean.TRUE); + return getConfigValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class, Boolean.FALSE); } private T getConfigValue(final String key, final Class valueType, From 38f3b531bde664aa6ccf0ff8f6ee635f41ffe98a Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Wed, 8 May 2019 16:20:43 +0200 Subject: [PATCH 35/56] Prevent Multi-Assignments feature from being disabled via Mgmt REST API Signed-off-by: Stefan Behl --- .../hawkbit/exception/SpServerError.java | 10 ++++- ...urationValueChangeNotAllowedException.java | 43 +++++++++++++++++++ .../jpa/JpaTenantConfigurationManagement.java | 15 +++++++ .../exception/ResponseExceptionHandler.java | 1 + 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/TenantConfigurationValueChangeNotAllowedException.java diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java index e685ba590a..7863a1f00b 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/exception/SpServerError.java @@ -191,6 +191,7 @@ public enum SpServerError { */ SP_CONFIGURATION_VALUE_INVALID("hawkbit.server.error.configValueInvalid", "The given configuration value is invalid."), + /** * */ @@ -232,7 +233,14 @@ public enum SpServerError { * invalid. */ SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID("hawkbit.server.error.repo.invalidAutoAssignDistributionSet", - "The given distribution set for auto-assignment is invalid: it is either incomplete (i.e. mandatory modules are missing) or soft deleted"); + "The given distribution set for auto-assignment is invalid: it is either incomplete (i.e. mandatory modules are missing) or soft deleted"), + + /** + * Error message informing the user that the requested tenant configuration + * change is not allowed. + */ + SP_CONFIGURATION_VALUE_CHANGE_NOT_ALLOWED("hawkbit.server.error.repo.tenantConfigurationValueChangeNotAllowed", + "The requested tenant configuration value modification is not allowed."); private final String key; private final String message; diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/TenantConfigurationValueChangeNotAllowedException.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/TenantConfigurationValueChangeNotAllowedException.java new file mode 100644 index 0000000000..8471179d1c --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/exception/TenantConfigurationValueChangeNotAllowedException.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2019 Bosch Software Innovations 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.repository.exception; + +import org.eclipse.hawkbit.exception.AbstractServerRtException; +import org.eclipse.hawkbit.exception.SpServerError; + +/** + * Exception which is supposed to be thrown if a property value is valid but + * cannot be set in the current context. + */ +public class TenantConfigurationValueChangeNotAllowedException extends AbstractServerRtException { + + private static final long serialVersionUID = 1L; + + /** + * Creates a new exception for the + * {@link SpServerError#SP_CONFIGURATION_VALUE_CHANGE_NOT_ALLOWED} error + * case. + */ + public TenantConfigurationValueChangeNotAllowedException() { + super(SpServerError.SP_CONFIGURATION_VALUE_CHANGE_NOT_ALLOWED); + } + + /** + * Creates a new exception for the + * {@link SpServerError#SP_CONFIGURATION_VALUE_CHANGE_NOT_ALLOWED} error + * case. + * + * @param message + * A custom error message. + */ + public TenantConfigurationValueChangeNotAllowedException(final String message) { + super(message, SpServerError.SP_CONFIGURATION_VALUE_CHANGE_NOT_ALLOWED); + } + +} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java index 0249af8564..5a71f0e1de 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java @@ -8,9 +8,12 @@ */ package org.eclipse.hawkbit.repository.jpa; +import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED; + import java.io.Serializable; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; +import org.eclipse.hawkbit.repository.exception.TenantConfigurationValueChangeNotAllowedException; import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.model.JpaTenantConfiguration; import org.eclipse.hawkbit.repository.model.TenantConfiguration; @@ -18,6 +21,8 @@ import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties; import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; import org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationValidatorException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; @@ -37,6 +42,8 @@ @Validated public class JpaTenantConfigurationManagement implements TenantConfigurationManagement { + private static final Logger LOG = LoggerFactory.getLogger(JpaTenantConfigurationManagement.class); + @Autowired private TenantConfigurationRepository tenantConfigurationRepository; @@ -157,6 +164,14 @@ public TenantConfigurationValue addOrUpdateConfigura tenantConfiguration.setValue(value.toString()); } + // prevent the Multi-Assignment feature from being disabled; this code + // path can be removed once we support this case + if (MULTI_ASSIGNMENTS_ENABLED.equals(configurationKeyName) + && !Boolean.parseBoolean(tenantConfiguration.getValue())) { + LOG.debug("The Multi-Assignments '{}' feature cannot be disabled.", configurationKeyName); + throw new TenantConfigurationValueChangeNotAllowedException(); + } + final JpaTenantConfiguration updatedTenantConfiguration = tenantConfigurationRepository .save(tenantConfiguration); diff --git a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java index 856e3a39eb..a72e809202 100644 --- a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java +++ b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/exception/ResponseExceptionHandler.java @@ -78,6 +78,7 @@ public class ResponseExceptionHandler { ERROR_TO_HTTP_STATUS.put(SpServerError.SP_TARGET_ATTRIBUTES_INVALID, HttpStatus.BAD_REQUEST); ERROR_TO_HTTP_STATUS.put(SpServerError.SP_AUTO_ASSIGN_ACTION_TYPE_INVALID, HttpStatus.BAD_REQUEST); ERROR_TO_HTTP_STATUS.put(SpServerError.SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID, HttpStatus.BAD_REQUEST); + ERROR_TO_HTTP_STATUS.put(SpServerError.SP_CONFIGURATION_VALUE_CHANGE_NOT_ALLOWED, HttpStatus.FORBIDDEN); } private static HttpStatus getStatusOrDefault(final SpServerError error) { From 7524abe1de19b5750bee8ecb419fab6402f22d39 Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Thu, 9 May 2019 10:45:40 +0200 Subject: [PATCH 36/56] add license header, remove unused instance variables Signed-off-by: Stefan Klotz --- .../model/DistributionSetAssignmentResultMap.java | 8 ++++++++ .../hawkbit/repository/jpa/JpaDeploymentManagement.java | 6 ------ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java index 7f47aa3597..a234f97d3d 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java @@ -1,3 +1,11 @@ +/** + * Copyright (c) 2019 Bosch Software Innovations 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.repository.model; import java.util.HashMap; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index 676f3dd473..a649728450 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -126,9 +126,6 @@ public class JpaDeploymentManagement implements DeploymentManagement { private final ActionStatusRepository actionStatusRepository; private final TargetManagement targetManagement; private final AuditorAware auditorProvider; - private final ApplicationEventPublisher eventPublisher; - private final BusProperties bus; - private final AfterTransactionCommitExecutor afterCommit; private final VirtualPropertyReplacer virtualPropertyReplacer; private final PlatformTransactionManager txManager; private final OnlineDsAssignmentStrategy onlineDsAssignmentStrategy; @@ -154,9 +151,6 @@ protected JpaDeploymentManagement(final EntityManager entityManager, final Actio this.actionStatusRepository = actionStatusRepository; this.targetManagement = targetManagement; this.auditorProvider = auditorProvider; - this.eventPublisher = eventPublisher; - this.bus = bus; - this.afterCommit = afterCommit; this.virtualPropertyReplacer = virtualPropertyReplacer; this.txManager = txManager; onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, afterCommit, eventPublisher, bus, From c54783f453d5ff32022b323b2d4b517ee8a6701a Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Thu, 9 May 2019 11:04:57 +0200 Subject: [PATCH 37/56] fix tenantConfigurationManagement mock in test Signed-off-by: Stefan Klotz --- .../org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java index c8ec04b60e..48b79e72a8 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java @@ -140,7 +140,7 @@ public void before() throws Exception { when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter); when(artifactManagementMock.findFirstBySHA1(SHA1)).thenReturn(Optional.empty()); when(tenantConfigurationManagement.getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class, - Boolean.TRUE)).thenReturn(false); + Boolean.FALSE)).thenReturn(false); final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(); final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware); From 8fba9e52066526d90d2399dbcebcd0cbfc44c860 Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Thu, 9 May 2019 15:21:47 +0200 Subject: [PATCH 38/56] return empty list instead of null Signed-off-by: Stefan Klotz --- .../org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java index bd6c548b2a..1e6681d2cf 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java @@ -259,7 +259,8 @@ private Map> getSoftwareModulesWith .findTargetVisibleMetaDataBySoftwareModuleId(smIds); return distributionSet.getModules().stream() - .collect(Collectors.toMap(sm -> sm, sm -> metadata.get(sm.getId()))); + .collect(Collectors.toMap(sm -> sm, + sm -> metadata.containsKey(sm.getId()) ? metadata.get(sm.getId()) : Collections.emptyList())); } /** From d2e838405325aaba881ee740e38353da0967cafe Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Fri, 10 May 2019 12:38:57 +0200 Subject: [PATCH 39/56] add ddi test for multiassignment, fix old test Signed-off-by: Stefan Klotz --- .../rest/resource/DdiRootControllerTest.java | 116 +++++++++++------- 1 file changed, 69 insertions(+), 47 deletions(-) diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java index 2a431802a6..8f1a595c1a 100644 --- a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java +++ b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java @@ -29,7 +29,9 @@ import java.util.Collections; import java.util.Map; +import java.util.UUID; +import org.apache.commons.lang3.RandomStringUtils; import org.eclipse.hawkbit.ddi.rest.api.DdiRestConstants; import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; @@ -258,9 +260,8 @@ public void rootRsNotModified() throws Exception { etagWithFirstUpdate)).andDo(MockMvcResultPrinter.print()).andExpect(status().isNotModified()); // now lets finish the update - sendDeploymentActionFeedback(target, updateAction, - JsonBuilder.deploymentActionFeedback(updateAction.getId().toString(), "closed")) - .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); + sendDeploymentActionFeedback(target, updateAction, "closed", null, null).andDo(MockMvcResultPrinter.print()) + .andExpect(status().isOk()); // we are again at the original state mvc.perform(get("/{tenant}/controller/v1/4711", tenantAware.getCurrentTenant()).header("If-None-Match", etag)) @@ -364,7 +365,6 @@ public void rootRsIpAddressNotStoredIfDisabled() throws Exception { @Expect(type = TargetAssignDistributionSetEvent.class, count = 1), @Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), - @Expect(type = TargetAttributesRequestedEvent.class, count = 1), @Expect(type = SoftwareModuleCreatedEvent.class, count = 3) }) public void tryToFinishAnUpdateProcessAfterItHasBeenFinished() throws Exception { final DistributionSet ds = testdataFactory.createDistributionSet(""); @@ -373,17 +373,14 @@ public void tryToFinishAnUpdateProcessAfterItHasBeenFinished() throws Exception .next(); final Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId()) .getContent().get(0); - sendDeploymentActionFeedback(savedTarget, savedAction, - JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "proceeding")) - .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); + sendDeploymentActionFeedback(savedTarget, savedAction, "proceeding", null, null) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); - sendDeploymentActionFeedback(savedTarget, savedAction, - JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "closed", "failure")) - .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); + sendDeploymentActionFeedback(savedTarget, savedAction, "closed", "failure", null) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); - sendDeploymentActionFeedback(savedTarget, savedAction, - JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "closed", "success")) - .andDo(MockMvcResultPrinter.print()).andExpect(status().isGone()); + sendDeploymentActionFeedback(savedTarget, savedAction, "closed", "success", null) + .andDo(MockMvcResultPrinter.print()).andExpect(status().isGone()); } @Test @@ -419,9 +416,7 @@ private void assertAttributesUpdateNotRequestedAfterFailedDeployment(Target targ target = assignDistributionSet(ds.getId(), target.getControllerId()).getAssignedEntity().iterator().next(); final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId()) .getContent().get(0); - sendDeploymentActionFeedback(target, action, - JsonBuilder.deploymentActionFeedback(action.getId().toString(), "closed", "failure", "")) - .andExpect(status().isOk()); + sendDeploymentActionFeedback(target, action, "closed", "failure", null).andExpect(status().isOk()); assertThatAttributesUpdateIsNotRequested(target.getControllerId()); } @@ -431,8 +426,7 @@ private void assertAttributesUpdateRequestedAfterSuccessfulDeployment(Target tar target = assignDistributionSet(ds.getId(), target.getControllerId()).getAssignedEntity().iterator().next(); final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId()) .getContent().get(0); - sendDeploymentActionFeedback(target, action, - JsonBuilder.deploymentActionFeedback(action.getId().toString(), "closed")).andExpect(status().isOk()); + sendDeploymentActionFeedback(target, action, "closed", null, null).andExpect(status().isOk()); assertThatAttributesUpdateIsRequested(target.getControllerId()); } @@ -448,8 +442,14 @@ private void assertThatAttributesUpdateIsNotRequested(final String targetControl .andExpect(jsonPath("$._links.configData").doesNotExist()); } - private ResultActions sendDeploymentActionFeedback(final Target target, final Action action, final String feedback) - throws Exception { + private ResultActions sendDeploymentActionFeedback(final Target target, final Action action, final String execution, + String finished, String message) throws Exception { + if (finished == null) + finished = "none"; + if (message == null) + message = RandomStringUtils.randomAlphanumeric(1000); + final String feedback = JsonBuilder.deploymentActionFeedback(action.getId().toString(), execution, finished, + message); return mvc.perform(post("/{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}/feedback", tenantAware.getCurrentTenant(), target.getControllerId(), action.getId()).content(feedback) .contentType(MediaType.APPLICATION_JSON)); @@ -472,19 +472,16 @@ public void testActionHistoryCount() throws Exception { final Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId()) .getContent().get(0); - sendDeploymentActionFeedback(savedTarget, savedAction, - JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "scheduled", - TARGET_SCHEDULED_INSTALLATION_MSG)).andDo(MockMvcResultPrinter.print()) + sendDeploymentActionFeedback(savedTarget, savedAction, "scheduled", null, TARGET_SCHEDULED_INSTALLATION_MSG) + .andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()); - sendDeploymentActionFeedback(savedTarget, savedAction, - JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "proceeding", - TARGET_PROCEEDING_INSTALLATION_MSG)).andDo(MockMvcResultPrinter.print()) + sendDeploymentActionFeedback(savedTarget, savedAction, "proceeding", null, TARGET_PROCEEDING_INSTALLATION_MSG) + .andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()); - sendDeploymentActionFeedback(savedTarget, savedAction, - JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "closed", "success", - TARGET_COMPLETED_INSTALLATION_MSG)).andDo(MockMvcResultPrinter.print()) + sendDeploymentActionFeedback(savedTarget, savedAction, "closed", "success", TARGET_COMPLETED_INSTALLATION_MSG) + .andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()); mvc.perform(get("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "?actionHistory=3", @@ -514,19 +511,16 @@ public void testActionHistoryZeroInput() throws Exception { final Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId()) .getContent().get(0); - sendDeploymentActionFeedback(savedTarget, savedAction, - JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "scheduled", - TARGET_SCHEDULED_INSTALLATION_MSG)).andDo(MockMvcResultPrinter.print()) + sendDeploymentActionFeedback(savedTarget, savedAction, "scheduled", null, TARGET_SCHEDULED_INSTALLATION_MSG) + .andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()); - sendDeploymentActionFeedback(savedTarget, savedAction, - JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "proceeding", - TARGET_PROCEEDING_INSTALLATION_MSG)).andDo(MockMvcResultPrinter.print()) + sendDeploymentActionFeedback(savedTarget, savedAction, "proceeding", null, TARGET_PROCEEDING_INSTALLATION_MSG) + .andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()); - sendDeploymentActionFeedback(savedTarget, savedAction, - JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "closed", "success", - TARGET_COMPLETED_INSTALLATION_MSG)).andDo(MockMvcResultPrinter.print()) + sendDeploymentActionFeedback(savedTarget, savedAction, "closed", "success", TARGET_COMPLETED_INSTALLATION_MSG) + .andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()); mvc.perform(get("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "?actionHistory=-2", @@ -552,19 +546,16 @@ public void testActionHistoryNegativeInput() throws Exception { final Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId()) .getContent().get(0); - sendDeploymentActionFeedback(savedTarget, savedAction, - JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "scheduled", - TARGET_SCHEDULED_INSTALLATION_MSG)).andDo(MockMvcResultPrinter.print()) + sendDeploymentActionFeedback(savedTarget, savedAction, "scheduled", null, TARGET_SCHEDULED_INSTALLATION_MSG) + .andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()); - sendDeploymentActionFeedback(savedTarget, savedAction, - JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "proceeding", - TARGET_PROCEEDING_INSTALLATION_MSG)).andDo(MockMvcResultPrinter.print()) + sendDeploymentActionFeedback(savedTarget, savedAction, "proceeding", null, TARGET_PROCEEDING_INSTALLATION_MSG) + .andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()); - sendDeploymentActionFeedback(savedTarget, savedAction, - JsonBuilder.deploymentActionFeedback(savedAction.getId().toString(), "closed", "success", - TARGET_COMPLETED_INSTALLATION_MSG)).andDo(MockMvcResultPrinter.print()) + sendDeploymentActionFeedback(savedTarget, savedAction, "closed", "success", TARGET_COMPLETED_INSTALLATION_MSG) + .andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()); mvc.perform(get("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "?actionHistory=-1", @@ -665,4 +656,35 @@ public void downloadAndUpdateStatusDuringMaintenanceWindow() throws Exception { .andExpect(jsonPath("$.deployment.update", equalTo("forced"))) .andExpect(jsonPath("$.deployment.maintenanceWindow", equalTo("available"))); } + + @Test + @Description("Assign multiple DS in multiassignment mode. The earliest active Action is exposed to the target.") + public void earliestActionIsExposedToTargetInMultiAssignMode() throws Exception { + setMultiAssignmentsEnabled(); + final Target target = testdataFactory.createTarget(); + final DistributionSet ds1 = testdataFactory.createDistributionSet(UUID.randomUUID().toString()); + final DistributionSet ds2 = testdataFactory.createDistributionSet(UUID.randomUUID().toString()); + final Action action1 = assignDistributionSet(ds1, target).getActions().get(0); + final Action action2 = assignDistributionSet(ds2, target).getActions().get(0); + + assertDeploymentActionIsExposedToTarget(target.getControllerId(), action1.getId()); + sendDeploymentActionFeedback(target, action1, "closed", "success", null); + assertDeploymentActionIsExposedToTarget(target.getControllerId(), action2.getId()); + + } + + public void assertDeploymentActionIsExposedToTarget(final String controllerId, final long expectedActionId) + throws Exception { + final String expectedDeploymentBaseLink = String.format("/%s/controller/v1/%s/deploymentBase/%d", + tenantAware.getCurrentTenant(), controllerId, expectedActionId); + mvc.perform(get("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(), + controllerId).accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._links.deploymentBase.href", containsString(expectedDeploymentBaseLink))); + + } + + private void setMultiAssignmentsEnabled() { + tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, true); + } } From 0d0953751937a39f18543ba81b1040573fd1acda Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Fri, 10 May 2019 14:34:55 +0200 Subject: [PATCH 40/56] Prevent autoclose from being modified if Multi-Assignments is enabled # WARNING: head commit changed in the meantime Signed-off-by: Stefan Behl --- .../amqp/AmqpMessageDispatcherService.java | 17 +++++++++-------- .../hawkbit/amqp/AmqpMessageHandlerService.java | 12 +++++------- .../hawkbit/repository/RepositoryConstants.java | 5 +++++ .../jpa/JpaTenantConfigurationManagement.java | 11 +++++++++++ 4 files changed, 30 insertions(+), 15 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java index 1b2eafcbdc..4c7bcec716 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java @@ -77,7 +77,7 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { private static final Logger LOG = LoggerFactory.getLogger(AmqpMessageDispatcherService.class); - private static final int MAX_ACTIONS = 100; + private static final int MAX_ACTIONS = RepositoryConstants.MAX_ACTION_COUNT; private final ArtifactUrlHandler artifactUrlHandler; private final AmqpMessageSenderService amqpSenderService; @@ -229,7 +229,7 @@ private static DmfActionRequest createPlainActionRequest(final Action action) { actionRequest.setActionId(action.getId()); return actionRequest; } - + private DmfDownloadAndUpdateRequest createDownloadAndUpdateRequest(final Target target, final Action action, final Map> softwareModules) { final DmfDownloadAndUpdateRequest request = new DmfDownloadAndUpdateRequest(); @@ -244,10 +244,11 @@ private DmfDownloadAndUpdateRequest createDownloadAndUpdateRequest(final Target /** * Method to get the type of event depending on whether the action is a - * DOWNLOAD_ONLY action or if it has a valid maintenance window available - * or not based on defined maintenance schedule. In case of no maintenance - * schedule or if there is a valid window available, the topic {@link EventTopic#DOWNLOAD_AND_INSTALL} is - * returned else {@link EventTopic#DOWNLOAD} is returned. + * DOWNLOAD_ONLY action or if it has a valid maintenance window available or + * not based on defined maintenance schedule. In case of no maintenance + * schedule or if there is a valid window available, the topic + * {@link EventTopic#DOWNLOAD_AND_INSTALL} is returned else + * {@link EventTopic#DOWNLOAD} is returned. * * @param action * current action properties. @@ -255,8 +256,8 @@ private DmfDownloadAndUpdateRequest createDownloadAndUpdateRequest(final Target * @return {@link EventTopic} to use for message. */ private static EventTopic getEventTypeForTarget(final ActionProperties action) { - return (Action.ActionType.DOWNLOAD_ONLY.equals(action.getActionType()) || - !action.isMaintenanceWindowAvailable()) ? EventTopic.DOWNLOAD : EventTopic.DOWNLOAD_AND_INSTALL; + return (Action.ActionType.DOWNLOAD_ONLY.equals(action.getActionType()) + || !action.isMaintenanceWindowAvailable()) ? EventTopic.DOWNLOAD : EventTopic.DOWNLOAD_AND_INSTALL; } /** diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java index 1e6681d2cf..8d3c2bd5d5 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java @@ -70,6 +70,8 @@ public class AmqpMessageHandlerService extends BaseAmqpService { private static final Logger LOG = LoggerFactory.getLogger(AmqpMessageHandlerService.class); + private static final int MAX_ACTIONS = RepositoryConstants.MAX_ACTION_COUNT; + private final AmqpMessageDispatcherService amqpMessageDispatcherService; private final ControllerManagement controllerManagement; @@ -80,8 +82,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService { private final SystemSecurityContext systemSecurityContext; - private static final int MAX_ACTIONS = 100; - /** * Constructor. * @@ -243,8 +243,7 @@ private void sendOldestActionToTarget(final Target target) { if (action.isCancelingOrCanceled()) { amqpMessageDispatcherService.sendCancelMessageToTarget(target.getTenant(), target.getControllerId(), action.getId(), target.getAddress()); - } - else { + } else { amqpMessageDispatcherService.sendUpdateMessageToTarget(new ActionProperties(action), action.getTarget(), getSoftwareModulesWithMetadata(action.getDistributionSet())); } @@ -258,9 +257,8 @@ private Map> getSoftwareModulesWith final Map> metadata = controllerManagement .findTargetVisibleMetaDataBySoftwareModuleId(smIds); - return distributionSet.getModules().stream() - .collect(Collectors.toMap(sm -> sm, - sm -> metadata.containsKey(sm.getId()) ? metadata.get(sm.getId()) : Collections.emptyList())); + return distributionSet.getModules().stream().collect(Collectors.toMap(sm -> sm, + sm -> metadata.containsKey(sm.getId()) ? metadata.get(sm.getId()) : Collections.emptyList())); } /** diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryConstants.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryConstants.java index 9ec08272d7..b7a9723442 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryConstants.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/RepositoryConstants.java @@ -32,6 +32,11 @@ public final class RepositoryConstants { */ public static final int DEFAULT_DS_TYPES_IN_TENANT = 3; + /** + * Maximum number of actions that can be retrieved. + */ + public static final int MAX_ACTION_COUNT = 100; + /** * Maximum number of messages that can be retrieved by a controller for an * {@link Action}. diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java index 5a71f0e1de..cafd7045bd 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java @@ -9,6 +9,7 @@ package org.eclipse.hawkbit.repository.jpa; 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 java.io.Serializable; @@ -172,6 +173,16 @@ public TenantConfigurationValue addOrUpdateConfigura throw new TenantConfigurationValueChangeNotAllowedException(); } + // prevent the Auto-Close property from being changed if + // Multi-Assignments is enabled + if (REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED.equals(configurationKeyName) + && getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue()) { + LOG.debug( + "The property '{}' must not be changed because the Multi-Assignments feature is currently enabled.", + configurationKeyName); + throw new TenantConfigurationValueChangeNotAllowedException(); + } + final JpaTenantConfiguration updatedTenantConfiguration = tenantConfigurationRepository .save(tenantConfiguration); From 89d1c80860b6678fdcc0ea1d3459be971fb924d2 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Fri, 10 May 2019 14:39:40 +0200 Subject: [PATCH 41/56] Add test for autoClose /multiAssign Signed-off-by: Stefan Behl --- .../MgmtTenantManagementResourceTest.java | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java index ec2b1d1589..42dec09f70 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java @@ -32,16 +32,42 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiInteg @Test @Description("Multiassignment can not be deactivated.") public void deactivateMultiassignment() throws Exception { - final String multiassignmentKey = "multi.assignments.enabled"; + final String multiAssignmentKey = "multi.assignments.enabled"; final String bodyActivate = new JSONObject().put("value", true).toString(); final String bodyDeactivate = new JSONObject().put("value", false).toString(); - mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", multiassignmentKey) + mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", multiAssignmentKey) .content(bodyActivate).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()); - mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", multiassignmentKey) + mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", multiAssignmentKey) .content(bodyDeactivate).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isForbidden()); } + + @Test + @Description("The 'repository.actions.autoclose.enabled' property must not be modified if Multi-Assignments is enabled.") + public void autoCloseCannotBeModifiedIfMultiAssignmentIsEnabled() throws Exception { + final String multiAssignmentKey = "multi.assignments.enabled"; + final String autoCloseKey = "repository.actions.autoclose.enabled"; + + final String bodyActivate = new JSONObject().put("value", true).toString(); + final String bodyDeactivate = new JSONObject().put("value", false).toString(); + + // enable Multi-Assignments + mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", multiAssignmentKey) + .content(bodyActivate).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) + .andExpect(status().isOk()); + + // try to enable Auto-Close + mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", autoCloseKey) + .content(bodyActivate).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) + .andExpect(status().isForbidden()); + + // try to disable Auto-Close + mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", autoCloseKey) + .content(bodyDeactivate).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) + .andExpect(status().isForbidden()); + } + } From c573c906e260427233bf5399f63010bdaee98255 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Fri, 10 May 2019 15:12:56 +0200 Subject: [PATCH 42/56] Javadoc improvements Signed-off-by: Stefan Behl --- .../amqp/AmqpMessageDispatcherService.java | 15 ++++++--------- .../dmf/json/model/DmfMultiActionRequest.java | 3 +++ .../hawkbit/repository/ControllerManagement.java | 10 ++++++++++ .../model/DistributionSetAssignmentResult.java | 11 ++++++----- .../model/DistributionSetAssignmentResultMap.java | 3 +++ .../MultiAssignmentsConfigurationItem.java | 8 ++++++++ 6 files changed, 36 insertions(+), 14 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java index 4c7bcec716..c32e265195 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java @@ -261,16 +261,13 @@ private static EventTopic getEventTypeForTarget(final ActionProperties action) { } /** - * Method to get the type of event depending on whether the action has a - * valid maintenance window available or not based on defined maintenance - * schedule. In case of no maintenance schedule or if there is a valid - * window available, the topic {@link EventTopic#DOWNLOAD_AND_INSTALL} is - * returned else {@link EventTopic#DOWNLOAD} is returned. + * Determines the {@link EventTopic} for the given {@link Action}, depending + * on its action type. * - * @param maintenanceWindowAvailable - * valid maintenance window or not. + * @param action + * to obtain the corresponding {@link EventTopic} for * - * @return {@link EventTopic} to use for message. + * @return the {@link EventTopic} for this action */ private static EventTopic getEventTypeForAction(final Action action) { if (action.isCancelingOrCanceled()) { @@ -284,7 +281,7 @@ private static EventTopic getEventTypeForAction(final Action action) { * the Distribution set to a Target has been canceled. * * @param cancelEvent - * the object to be send. + * that is to be converted to a DMF message */ @EventListener(classes = CancelTargetAssignmentEvent.class) protected void targetCancelAssignmentToDistributionSet(final CancelTargetAssignmentEvent cancelEvent) { diff --git a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java index 0d6bd36a21..9f98d331cd 100644 --- a/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java +++ b/hawkbit-dmf/hawkbit-dmf-api/src/main/java/org/eclipse/hawkbit/dmf/json/model/DmfMultiActionRequest.java @@ -59,6 +59,9 @@ public void addElement(final EventTopic topic, final DmfActionRequest action) { addElement(element); } + /** + * Represents an element within a {@link DmfMultiActionRequest}. + */ public static class DmfMultiActionElement { @JsonProperty diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java index cf05b0b56a..2c982e60b6 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/ControllerManagement.java @@ -151,6 +151,16 @@ Map> findTargetVisibleMetaDataBySoftwareModul @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) Optional findOldestActiveActionByTarget(@NotEmpty String controllerId); + /** + * Retrieves all active actions which are assigned to the target with the + * given controller ID. + * + * @param pageable + * pagination parameter + * @param controllerId + * of the target + * @return the requested {@link Page} with {@link Action}s + */ @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) Page findActiveActionsByTarget(@NotNull Pageable pageable, @NotEmpty String controllerId); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResult.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResult.java index b6f2d1a4ec..e0aa0fc57c 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResult.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResult.java @@ -34,6 +34,8 @@ public class DistributionSetAssignmentResult extends AssignmentResult { * * Constructor. * + * @param distributionSet + * that has been assigned * @param assignedTargets * the target objects which have been assigned to the * distribution set @@ -41,15 +43,14 @@ public class DistributionSetAssignmentResult extends AssignmentResult { * count of the assigned targets * @param alreadyAssigned * the count of the already assigned targets - * @param targetManagement - * to retrieve the assigned targets * @param actions * of the assignment - * + * @param targetManagement + * to retrieve the assigned targets */ public DistributionSetAssignmentResult(final DistributionSet distributionSet, final List assignedTargets, - final int assigned, - final int alreadyAssigned, final List actions, final TargetManagement targetManagement) { + final int assigned, final int alreadyAssigned, final List actions, + final TargetManagement targetManagement) { super(assigned, alreadyAssigned, 0, Collections.emptyList(), Collections.emptyList()); this.distributionSet = distributionSet; this.assignedTargets = assignedTargets; diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java index a234f97d3d..6372ef6d0f 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java @@ -13,6 +13,9 @@ import java.util.Map.Entry; import java.util.Set; +/** + * Represents a map of {@link DistributionSetAssignmentResultMap} entities. + */ public class DistributionSetAssignmentResultMap { private final Map results = new HashMap<>(); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/repository/MultiAssignmentsConfigurationItem.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/repository/MultiAssignmentsConfigurationItem.java index 8fee392d2e..b65378ad6c 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/repository/MultiAssignmentsConfigurationItem.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/repository/MultiAssignmentsConfigurationItem.java @@ -34,6 +34,14 @@ public class MultiAssignmentsConfigurationItem extends AbstractBooleanTenantConf private boolean isMultiAssignmentsEnabled; private boolean multiAssignmentsEnabledChanged; + /** + * Constructor. + * + * @param tenantConfigurationManagement + * to read /write tenant-specific configuration properties + * @param i18n + * to obtain localized strings + */ public MultiAssignmentsConfigurationItem(final TenantConfigurationManagement tenantConfigurationManagement, final VaadinMessageSource i18n) { super(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, tenantConfigurationManagement, i18n); From 3f651c58852b9ef4cd958c6937a856ada65ef46a Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Fri, 10 May 2019 15:52:04 +0200 Subject: [PATCH 43/56] change test method that waits for dmf messages to be dispatched Signed-off-by: Stefan Klotz --- .../AbstractAmqpServiceIntegrationTest.java | 18 ++------- ...ssageDispatcherServiceIntegrationTest.java | 40 +++++++++---------- .../integration/listener/ReplyToListener.java | 11 +++++ 3 files changed, 34 insertions(+), 35 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java index a6e2ad184f..bd74dff9a8 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java @@ -110,23 +110,11 @@ protected T waitUntilIsPresent(final Callable> callable) { } } - /** - * Checks the total number of {@link Message} with type - * {@link MessageType#EVENT} that has been received by the - * {@link ReplyToListener} since the beginning of the test. Waits for the - * exact number, times out when there are more messages than expected. - * - * @param numberOfMessages - * exact number of expected messages - */ - protected void waitUntilEventMessagesAreSent(final int numberOfMessages) { + protected void waitUntilEventMessagesAreDispatchedToTarget(final EventTopic... eventTopics) { createConditionFactory().untilAsserted(() -> { - int messagesReceived = 0; - for (final List messages : replyToListener.getEventMessages().values()) { - messagesReceived += messages.size(); - } - assertThat(messagesReceived).isEqualTo(numberOfMessages); + assertThat(eventTopics).containsExactlyInAnyOrderElementsOf(replyToListener.getLatestEventMessageTopics()); }); + replyToListener.resetLatestEventMessageTopics(); } protected void purgeReplyToListener() { diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java index 2933e6eebe..a361c5735d 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java @@ -170,12 +170,12 @@ public void assignMultipleDsInMultiAssignMode() { final Long actionId1 = assignNewDsToTarget(controllerId); final Entry action1Install = new SimpleEntry<>(actionId1, EventTopic.DOWNLOAD_AND_INSTALL); - waitUntilEventMessagesAreSent(1); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION); assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Install)); final Long actionId2 = assignNewDsToTarget(controllerId); final Entry action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL); - waitUntilEventMessagesAreSent(2); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION); assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Install, action2Install)); } @@ -196,9 +196,9 @@ public void cancelActionInMultiAssignMode() { final long actionId1 = assignNewDsToTarget(controllerId); final long actionId2 = assignNewDsToTarget(controllerId); - waitUntilEventMessagesAreSent(2); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION, EventTopic.MULTI_ACTION); deploymentManagement.cancelAction(actionId1); - waitUntilEventMessagesAreSent(3); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION); final Entry action1Cancel = new SimpleEntry<>(actionId1, EventTopic.CANCEL_DOWNLOAD); final Entry action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL); @@ -206,7 +206,7 @@ public void cancelActionInMultiAssignMode() { assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Cancel, action2Install)); updateActionViaDmfClient(controllerId, actionId1, DmfActionStatus.CANCELED); - waitUntilEventMessagesAreSent(4); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION); assertLatestMultiActionMessage(controllerId, Arrays.asList(action2Install)); } @@ -229,10 +229,11 @@ public void finishActionInMultiAssignMode() { final long actionId1 = assignNewDsToTarget(controllerId); final long actionId2 = assignNewDsToTarget(controllerId); final Entry action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL); - waitUntilEventMessagesAreSent(2); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION, EventTopic.MULTI_ACTION); updateActionViaDmfClient(controllerId, actionId1, DmfActionStatus.FINISHED); - waitUntilEventMessagesAreSent(4); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.REQUEST_ATTRIBUTES_UPDATE, + EventTopic.MULTI_ACTION); assertRequestAttributesUpdateMessage(controllerId); assertLatestMultiActionMessage(controllerId, Arrays.asList(action2Install)); } @@ -254,9 +255,9 @@ public void assignDsMultipleTimesInMultiAssignMode() { final DistributionSet ds = testdataFactory.createDistributionSet(UUID.randomUUID().toString()); final Long actionId1 = assignDistributionSet(ds.getId(), controllerId).getActionIds().get(0); - waitUntilEventMessagesAreSent(1); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION); final Long actionId2 = assignDistributionSet(ds.getId(), controllerId).getActionIds().get(0); - waitUntilEventMessagesAreSent(2); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION); final Entry action1Install = new SimpleEntry<>(actionId1, EventTopic.DOWNLOAD_AND_INSTALL); final Entry action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL); @@ -297,11 +298,11 @@ public void startRolloutsWithSameDsInMultiAssignMode() { final String filterQuery = "controllerId==" + controllerId; startRollout(ds, filterQuery); - waitUntilEventMessagesAreSent(1); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION); assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds)); startRollout(ds, filterQuery); - waitUntilEventMessagesAreSent(2); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION); assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds, smIds)); } @@ -319,9 +320,9 @@ public void startRolloutsWithSameDsInMultiAssignMode() { @Expect(type = RolloutUpdatedEvent.class, count = 9), @Expect(type = RolloutGroupCreatedEvent.class, count = 3), @Expect(type = RolloutGroupUpdatedEvent.class, count = 6) }) - public void stratMultipleRolloutsAndFinishInMultiAssignMode() { + public void startMultipleRolloutsAndFinishInMultiAssignMode() { setMultiAssignmentsEnabled(true); - final String controllerId = TARGET_PREFIX + "stratMultipleRolloutsAndFinishInMultiAssignMode"; + final String controllerId = TARGET_PREFIX + "startMultipleRolloutsAndFinishInMultiAssignMode"; registerAndAssertTargetWithExistingTenant(controllerId); final String filterQuery = "controllerId==" + controllerId; @@ -332,9 +333,9 @@ public void stratMultipleRolloutsAndFinishInMultiAssignMode() { startRollout(ds1, filterQuery); startRollout(ds2, filterQuery); - waitUntilEventMessagesAreSent(2); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION, EventTopic.MULTI_ACTION); startRollout(ds1, filterQuery); - waitUntilEventMessagesAreSent(3); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION); assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds1, smIds2, smIds1)); final List installActions = getLatestLatestMultiActionMessageActions(controllerId).stream() @@ -342,13 +343,13 @@ public void stratMultipleRolloutsAndFinishInMultiAssignMode() { .collect(Collectors.toList()); updateActionViaDmfClient(controllerId, installActions.get(0), DmfActionStatus.FINISHED); - // sends REQUEST_ATTRIBUTES_UPDATE - waitUntilEventMessagesAreSent(5); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.REQUEST_ATTRIBUTES_UPDATE, + EventTopic.MULTI_ACTION); assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds2, smIds1)); updateActionViaDmfClient(controllerId, installActions.get(1), DmfActionStatus.FINISHED); - // sends REQUEST_ATTRIBUTES_UPDATE - waitUntilEventMessagesAreSent(7); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.REQUEST_ATTRIBUTES_UPDATE, + EventTopic.MULTI_ACTION); assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds1)); } @@ -435,7 +436,6 @@ public void downloadOnlyAssignmentSendsDownloadMessageTopic() { 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()); diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java index b3672483e5..172809edb7 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java @@ -30,6 +30,7 @@ public class ReplyToListener implements TestRabbitListener { public static final String REPLY_TO_QUEUE = "reply_queue"; private final Map> eventMessages = new EnumMap<>(EventTopic.class); + private final List latestEventMessageTopics = new ArrayList<>(); private final Map deleteMessages = new HashMap<>(); private final Map pingResponseMessages = new HashMap<>(); @@ -42,6 +43,7 @@ public void handleMessage(final Message message) { if (messageType == MessageType.EVENT) { final EventTopic eventTopic = EventTopic .valueOf(message.getMessageProperties().getHeaders().get(MessageHeaderKey.TOPIC).toString()); + latestEventMessageTopics.add(eventTopic); eventMessages.merge(eventTopic, Collections.singletonList(message), (oldList, listToAdd) -> { final List newList = new ArrayList<>(oldList); newList.addAll(listToAdd); @@ -72,6 +74,15 @@ public void purge() { eventMessages.clear(); deleteMessages.clear(); pingResponseMessages.clear(); + latestEventMessageTopics.clear(); + } + + public List getLatestEventMessageTopics() { + return latestEventMessageTopics; + } + + public void resetLatestEventMessageTopics() { + latestEventMessageTopics.clear(); } public Message getLatestEventMessage(final EventTopic eventTopic) { From 6aeca2612828214ddd32ecb5b6a5b42df8e7423d Mon Sep 17 00:00:00 2001 From: Stefan Klotz Date: Mon, 13 May 2019 10:09:39 +0200 Subject: [PATCH 44/56] clean up code Signed-off-by: Stefan Klotz --- .../AbstractAmqpServiceIntegrationTest.java | 6 +-- ...ssageDispatcherServiceIntegrationTest.java | 38 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java index bd74dff9a8..a8784d36ba 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java @@ -255,11 +255,11 @@ private static boolean isDownloadAndUpdateRequest(final DmfMultiActionElement mu protected void assertLatestMultiActionMessage(final String controllerId, final List> actionsExpected) { - final List> actionsFromMessage = getLatestLatestMultiActionMessageActions(controllerId); - assertThat(actionsFromMessage).containsExactlyElementsOf(actionsExpected); + final List> actionsFromMessage = getLatestMultiActionMessageActions(controllerId); + assertThat(actionsFromMessage).containsExactlyInAnyOrderElementsOf(actionsExpected); } - protected List> getLatestLatestMultiActionMessageActions( + protected List> getLatestMultiActionMessageActions( final String expectedControllerId) { final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION); assertThat(multiactionMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID)) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java index a361c5735d..9f4bf8e99b 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java @@ -154,7 +154,7 @@ public void assignDistributionSetMultipleTimes() { } @Test - @Description("If multi assignment is enabled multi-action message are sent.") + @Description("If multi assignment is enabled multi-action messages are sent.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @Expect(type = DeploymentEvent.class, count = 2), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @@ -164,7 +164,7 @@ public void assignDistributionSetMultipleTimes() { @Expect(type = DistributionSetCreatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) }) public void assignMultipleDsInMultiAssignMode() { - setMultiAssignmentsEnabled(true); + enableMultiAssignments(); final String controllerId = TARGET_PREFIX + "assignMultipleDsInMultiAssignMode"; registerAndAssertTargetWithExistingTenant(controllerId); @@ -190,7 +190,7 @@ public void assignMultipleDsInMultiAssignMode() { @Expect(type = DistributionSetCreatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) }) public void cancelActionInMultiAssignMode() { - setMultiAssignmentsEnabled(true); + enableMultiAssignments(); final String controllerId = TARGET_PREFIX + "cancelActionInMultiAssignMode"; registerAndAssertTargetWithExistingTenant(controllerId); @@ -222,7 +222,7 @@ public void cancelActionInMultiAssignMode() { @Expect(type = DistributionSetCreatedEvent.class, count = 2), @Expect(type = TargetUpdatedEvent.class, count = 3), @Expect(type = TargetPollEvent.class, count = 1) }) public void finishActionInMultiAssignMode() { - setMultiAssignmentsEnabled(true); + enableMultiAssignments(); final String controllerId = TARGET_PREFIX + "finishActionInMultiAssignMode"; registerAndAssertTargetWithExistingTenant(controllerId); @@ -249,7 +249,7 @@ public void finishActionInMultiAssignMode() { @Expect(type = DistributionSetCreatedEvent.class, count = 1), @Expect(type = TargetUpdatedEvent.class, count = 2), @Expect(type = TargetPollEvent.class, count = 1) }) public void assignDsMultipleTimesInMultiAssignMode() { - setMultiAssignmentsEnabled(true); + enableMultiAssignments(); final String controllerId = TARGET_PREFIX + "assignDsMultipleTimesInMultiAssignMode"; registerAndAssertTargetWithExistingTenant(controllerId); final DistributionSet ds = testdataFactory.createDistributionSet(UUID.randomUUID().toString()); @@ -271,10 +271,11 @@ private void updateActionViaDmfClient(final String controllerId, final long acti private Long assignNewDsToTarget(final String controllerId) { final DistributionSet ds = testdataFactory.createDistributionSet(UUID.randomUUID().toString()); - return assignDistributionSet(ds.getId(), controllerId).getActionIds().get(0); + final Long actionId = assignDistributionSet(ds.getId(), controllerId).getActionIds().get(0); + waitUntilTargetHasStatus(controllerId, TargetUpdateStatus.PENDING); + return actionId; } - // TODO: check rollout event counts @Test @Description("If multi assignment is enabled multiple rollouts with the same DS lead to multiple actions.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @@ -289,7 +290,7 @@ private Long assignNewDsToTarget(final String controllerId) { @Expect(type = RolloutGroupCreatedEvent.class, count = 2), @Expect(type = RolloutGroupUpdatedEvent.class, count = 4) }) public void startRolloutsWithSameDsInMultiAssignMode() { - setMultiAssignmentsEnabled(true); + enableMultiAssignments(); final String controllerId = TARGET_PREFIX + "startRolloutsWithSameDsInMultiAssignMode"; registerAndAssertTargetWithExistingTenant(controllerId); @@ -297,16 +298,15 @@ public void startRolloutsWithSameDsInMultiAssignMode() { final Set smIds = getSoftwareModuleIds(ds); final String filterQuery = "controllerId==" + controllerId; - startRollout(ds, filterQuery); + createAndStartRollout(ds, filterQuery); waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION); assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds)); - startRollout(ds, filterQuery); + createAndStartRollout(ds, filterQuery); waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION); assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds, smIds)); } - // TODO: check rollout event counts @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), @@ -321,7 +321,7 @@ public void startRolloutsWithSameDsInMultiAssignMode() { @Expect(type = RolloutGroupCreatedEvent.class, count = 3), @Expect(type = RolloutGroupUpdatedEvent.class, count = 6) }) public void startMultipleRolloutsAndFinishInMultiAssignMode() { - setMultiAssignmentsEnabled(true); + enableMultiAssignments(); final String controllerId = TARGET_PREFIX + "startMultipleRolloutsAndFinishInMultiAssignMode"; registerAndAssertTargetWithExistingTenant(controllerId); @@ -331,14 +331,14 @@ public void startMultipleRolloutsAndFinishInMultiAssignMode() { final DistributionSet ds2 = testdataFactory.createDistributionSet(UUID.randomUUID().toString()); final Set smIds2 = getSoftwareModuleIds(ds2); - startRollout(ds1, filterQuery); - startRollout(ds2, filterQuery); + createAndStartRollout(ds1, filterQuery); + createAndStartRollout(ds2, filterQuery); waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION, EventTopic.MULTI_ACTION); - startRollout(ds1, filterQuery); + createAndStartRollout(ds1, filterQuery); waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION); assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds1, smIds2, smIds1)); - final List installActions = getLatestLatestMultiActionMessageActions(controllerId).stream() + final List installActions = getLatestMultiActionMessageActions(controllerId).stream() .filter(entry -> entry.getValue().equals(EventTopic.DOWNLOAD_AND_INSTALL)).map(Entry::getKey) .collect(Collectors.toList()); @@ -357,7 +357,7 @@ private Set getSoftwareModuleIds(final DistributionSet ds) { return ds.getModules().stream().map(SoftwareModule::getId).collect(Collectors.toSet()); } - private Rollout startRollout(final DistributionSet ds, final String filterQuery) { + 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()); @@ -465,8 +465,8 @@ private void waitUntil(final Callable callable) { createConditionFactory().until(() -> securityRule.runAsPrivileged(callable)); } - private void setMultiAssignmentsEnabled(final boolean enable) { + private void enableMultiAssignments() { tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, - enable); + true); } } From 9e7684b972b6e3f1f0ef6a199256d6497c9be6a1 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Tue, 14 May 2019 11:25:59 +0200 Subject: [PATCH 45/56] Fix UI-related PR review findings Signed-off-by: Stefan Behl --- .../TargetAssignmentOperations.java | 67 ++++++++------- .../management/dstable/DistributionTable.java | 83 +++++++------------ .../management/state/AssignmentUIState.java | 80 ------------------ .../management/state/ManagementUIState.java | 6 -- .../management/targettable/TargetTable.java | 57 ++++++------- .../src/main/resources/messages.properties | 3 +- 6 files changed, 94 insertions(+), 202 deletions(-) delete mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/AssignmentUIState.java diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/TargetAssignmentOperations.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/TargetAssignmentOperations.java index 55ee09a84e..2b45ff6e30 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/TargetAssignmentOperations.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/TargetAssignmentOperations.java @@ -8,6 +8,7 @@ */ package org.eclipse.hawkbit.ui.management; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; @@ -17,8 +18,10 @@ import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper; import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException; import org.eclipse.hawkbit.repository.model.Action.ActionType; +import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult; import org.eclipse.hawkbit.repository.model.RepositoryModelConstants; +import org.eclipse.hawkbit.repository.model.Target; import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.eclipse.hawkbit.ui.UiProperties; import org.eclipse.hawkbit.ui.common.confirmwindow.layout.ConfirmationTab; @@ -30,7 +33,6 @@ import org.eclipse.hawkbit.ui.management.miscs.AbstractActionTypeOptionGroupLayout.ActionTypeOption; import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupAssignmentLayout; import org.eclipse.hawkbit.ui.management.miscs.MaintenanceWindowLayout; -import org.eclipse.hawkbit.ui.management.state.AssignmentUIState; import org.eclipse.hawkbit.ui.management.state.ManagementUIState; import org.eclipse.hawkbit.ui.utils.UIComponentIdProvider; import org.eclipse.hawkbit.ui.utils.UINotification; @@ -56,8 +58,12 @@ private TargetAssignmentOperations() { } /** - * Save all target(s)-distributionSet assignments + * Save the given distribution set assignments * + * @param targets + * to assign the given distribution sets to + * @param distributionSets + * to assign to the given targets * @param managementUIState * the management UI state * @param actionTypeOptionGroupLayout @@ -75,13 +81,16 @@ private TargetAssignmentOperations() { * @param eventSource * the source object for sending potential events */ - public static void saveAllAssignments(final ManagementUIState managementUIState, + public static void saveAllAssignments(final List targets, final List distributionSets, + final ManagementUIState managementUIState, final ActionTypeOptionGroupAssignmentLayout actionTypeOptionGroupLayout, final MaintenanceWindowLayout maintenanceWindowLayout, final DeploymentManagement deploymentManagement, final UINotification notification, final UIEventBus eventBus, final VaadinMessageSource i18n, final Object eventSource) { + final ActionType actionType = ((ActionTypeOption) actionTypeOptionGroupLayout.getActionTypeOptionGroup() .getValue()).getActionType(); + final long forcedTimeStamp = (((ActionTypeOption) actionTypeOptionGroupLayout.getActionTypeOptionGroup() .getValue()) == ActionTypeOption.AUTO_FORCED) ? actionTypeOptionGroupLayout.getForcedTimeDateField().getValue().getTime() @@ -91,45 +100,44 @@ public static void saveAllAssignments(final ManagementUIState managementUIState, final String maintenanceDuration = maintenanceWindowLayout.getMaintenanceDuration(); final String maintenanceTimeZone = maintenanceWindowLayout.getMaintenanceTimeZone(); - final AssignmentUIState assignmentState = managementUIState.getAssignmentState(); - assignmentState.forEach(distributionSetIdName -> { - final Set targetIds = assignmentState.getAssignedTargets(distributionSetIdName); - final DistributionSetAssignmentResult distributionSetAssignmentResult = deploymentManagement - .assignDistributionSet(distributionSetIdName.getId(), targetIds - .stream().map(t -> maintenanceWindowLayout.isEnabled() - ? new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp, - maintenanceSchedule, maintenanceDuration, maintenanceTimeZone) - : new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp)) - .collect(Collectors.toList())); + final Set dsIds = distributionSets.stream().map(DistributionSet::getId).collect(Collectors.toSet()); + final List trgActionType = targets.stream() + .map(t -> maintenanceWindowLayout.isEnabled() + ? new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp, + maintenanceSchedule, maintenanceDuration, maintenanceTimeZone) + : new TargetWithActionType(t.getControllerId(), actionType, forcedTimeStamp)) + .collect(Collectors.toList()); - if (distributionSetAssignmentResult.getAssigned() > 0) { - notification.displaySuccess( - i18n.getMessage("message.target.assignment", distributionSetAssignmentResult.getAssigned())); - } - if (distributionSetAssignmentResult.getAlreadyAssigned() > 0) { - notification.displaySuccess(i18n.getMessage("message.target.alreadyAssigned", - distributionSetAssignmentResult.getAlreadyAssigned())); - } - }); + final List results = deploymentManagement.assignDistributionSets(dsIds, + trgActionType); + + // use the last one for the notification box + final DistributionSetAssignmentResult assignmentResult = results.get(results.size() - 1); + if (assignmentResult.getAssigned() > 0) { + notification.displaySuccess(i18n.getMessage("message.target.assignment", assignmentResult.getAssigned())); + } + if (assignmentResult.getAlreadyAssigned() > 0) { + notification.displaySuccess( + i18n.getMessage("message.target.alreadyAssigned", assignmentResult.getAlreadyAssigned())); + } - refreshPinnedDetails(assignmentState, managementUIState, eventBus, eventSource); + final Set targetIds = targets.stream().map(Target::getId).collect(Collectors.toSet()); + refreshPinnedDetails(dsIds, targetIds, managementUIState, eventBus, eventSource); - assignmentState.clear(); notification.displaySuccess(i18n.getMessage("message.target.ds.assign.success")); eventBus.publish(eventSource, SaveActionWindowEvent.SAVED_ASSIGNMENTS); } - private static void refreshPinnedDetails(final AssignmentUIState assignmentState, + private static void refreshPinnedDetails(final Set dsIds, final Set targetIds, final ManagementUIState managementUIState, final UIEventBus eventBus, final Object eventSource) { final Optional pinnedDist = managementUIState.getTargetTableFilters().getPinnedDistId(); final Optional pinnedTarget = managementUIState.getDistributionTableFilters().getPinnedTarget(); if (pinnedDist.isPresent()) { - final Long pinnedDistSet = pinnedDist.get(); - if (assignmentState.isAssigned(pinnedDistSet)) { + if (dsIds.contains(pinnedDist.get())) { eventBus.publish(eventSource, PinUnpinEvent.PIN_DISTRIBUTION); } - } else if (pinnedTarget.isPresent() && assignmentState.hasAssignments(pinnedTarget.get())) { + } else if (pinnedTarget.isPresent() && targetIds.contains(pinnedTarget.get().getTargetId())) { eventBus.publish(eventSource, PinUnpinEvent.PIN_TARGET); } } @@ -167,7 +175,8 @@ public static boolean isMaintenanceWindowValid(final MaintenanceWindowLayout mai * @param maintenanceWindowLayout * the Maintenance Window Layout * @param saveButtonToggle - * The event listener to derimne if save button should be enabled or not + * The event listener to derimne if save button should be enabled + * or not * @param i18n * the Vaadin Message Source for multi language * @param uiProperties diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java index 4d27240f73..ff25f4c579 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java @@ -51,7 +51,6 @@ import org.eclipse.hawkbit.ui.management.event.RefreshDistributionTableByFilterEvent; import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupAssignmentLayout; import org.eclipse.hawkbit.ui.management.miscs.MaintenanceWindowLayout; -import org.eclipse.hawkbit.ui.management.state.AssignmentUIState; import org.eclipse.hawkbit.ui.management.state.ManagementUIState; import org.eclipse.hawkbit.ui.management.targettable.TargetTable; import org.eclipse.hawkbit.ui.push.DistributionSetUpdatedEventContainer; @@ -411,83 +410,61 @@ private void assignTargetToDs(final DragAndDropEvent event) { assignTargetToDs(getItem(distItemId), targetManagement.get(targetIdSet)); } - private void assignTargetToDs(final Item item, final List targetDetailsList) { + private void assignTargetToDs(final Item item, final List targets) { if (item == null || item.getItemProperty("id") == null) { return; } - if (targetDetailsList.isEmpty()) { + if (targets.isEmpty()) { getNotification().displayWarning(getI18n().getMessage(TARGETS_NOT_EXISTS)); return; } final Long distId = (Long) item.getItemProperty("id").getValue(); selectDroppedEntities(distId); - final Optional findDistributionSetById = distributionSetManagement.get(distId); - if (!findDistributionSetById.isPresent()) { + final Optional distributionSet = distributionSetManagement.get(distId); + if (!distributionSet.isPresent()) { getNotification().displayWarning(getI18n().getMessage(DISTRIBUTIONSET_NOT_EXISTS)); return; } - addNewDistributionToAssignmentList(targetDetailsList, findDistributionSetById.get()); - openConfirmationWindowForAssignment(findDistributionSetById.get().getName(), targetDetailsList); - } - - private void addNewDistributionToAssignmentList(final List targetDetailsList, - final DistributionSet distributionSet) { - final DistributionSetIdName distributionSetIdName = new DistributionSetIdName(distributionSet); - String pendingActionMessage = null; - final AssignmentUIState assignmentState = managementUIState.getAssignmentState(); - for (final Target target : targetDetailsList) { - final TargetIdName targetIdName = new TargetIdName(target); - if (assignmentState.isAssignedTo(distributionSetIdName, targetIdName) && !isMultiAssignmentsEnabled()) { - pendingActionMessage = getPendingActionMessage(pendingActionMessage, target.getControllerId(), - HawkbitCommonUtil.getDistributionNameAndVersion(distributionSetIdName.getName(), - distributionSetIdName.getVersion())); - getNotification().displayValidationError(pendingActionMessage); - } else { - assignmentState.addAssignment(distributionSetIdName, targetIdName); - } - } + openConfirmationWindowForAssignment(distributionSet.get(), targets); } - private void openConfirmationWindowForAssignment(final String distributionNameToAssign, - final List targetDetailsList) { - final String confirmQuestion = createConfirmationQuestionForAssignment(distributionNameToAssign, - targetDetailsList); - createConfirmationWindowForAssignment(confirmQuestion); + private void openConfirmationWindowForAssignment(final DistributionSet distributionSet, + final List targets) { + + final String question = getAssignmentConfirmationMessage(distributionSet, targets); + final String caption = getI18n().getMessage(CAPTION_ENTITY_ASSIGN_ACTION_CONFIRMBOX); + final String okLabel = getI18n().getMessage(UIMessageIdProvider.BUTTON_OK); + final String cancelLabel = getI18n().getMessage(UIMessageIdProvider.BUTTON_CANCEL); + + confirmDialog = new ConfirmationDialog(caption, question, okLabel, cancelLabel, ok -> { + if (ok && isMaintenanceWindowValid(maintenanceWindowLayout, getNotification())) { + saveAllAssignments(targets, Collections.singletonList(distributionSet), managementUIState, + actionTypeOptionGroupLayout, maintenanceWindowLayout, deploymentManagement, getNotification(), + getEventBus(), getI18n(), this); + } + }, createAssignmentTab(actionTypeOptionGroupLayout, maintenanceWindowLayout, saveButtonToggle(), getI18n(), + uiProperties), UIComponentIdProvider.DIST_SET_TO_TARGET_ASSIGNMENT_CONFIRM_ID); + UI.getCurrent().addWindow(confirmDialog.getWindow()); confirmDialog.getWindow().bringToFront(); } - private void createConfirmationWindowForAssignment(final String confirmQuestion) { - confirmDialog = new ConfirmationDialog(getI18n().getMessage(CAPTION_ENTITY_ASSIGN_ACTION_CONFIRMBOX), - confirmQuestion, getI18n().getMessage(UIMessageIdProvider.BUTTON_OK), - getI18n().getMessage(UIMessageIdProvider.BUTTON_CANCEL), ok -> { - if (ok && isMaintenanceWindowValid(maintenanceWindowLayout, getNotification())) { - saveAllAssignments(managementUIState, actionTypeOptionGroupLayout, maintenanceWindowLayout, - deploymentManagement, getNotification(), getEventBus(), getI18n(), this); - } else { - managementUIState.getAssignmentState().clear(); - } - }, createAssignmentTab(actionTypeOptionGroupLayout, maintenanceWindowLayout, saveButtonToggle(), - getI18n(), uiProperties), - UIComponentIdProvider.DIST_SET_TO_TARGET_ASSIGNMENT_CONFIRM_ID); - } - private Consumer saveButtonToggle() { return isEnabled -> confirmDialog.getOkButton().setEnabled(isEnabled); } - private String createConfirmationQuestionForAssignment(final String distributionNameToAssign, - final List targetDetailsList) { - if (targetDetailsList.size() == 1) { - return getI18n().getMessage(MESSAGE_CONFIRM_ASSIGN_ENTITY, distributionNameToAssign, "target", - targetDetailsList.get(0).getName()); - } else { - return getI18n().getMessage(MESSAGE_CONFIRM_ASSIGN_MULTIPLE_ENTITIES, targetDetailsList.size(), "targets", - distributionNameToAssign); + private String getAssignmentConfirmationMessage(final DistributionSet distributionSet, final List targets) { + final String distributionName = distributionSet.getName(); + final int targetCount = targets.size(); + if (targetCount > 1) { + return getI18n().getMessage(MESSAGE_CONFIRM_ASSIGN_MULTIPLE_ENTITIES, targetCount, "targets", + distributionName); } + return getI18n().getMessage(MESSAGE_CONFIRM_ASSIGN_ENTITY, distributionName, "target", + targets.get(0).getName()); } @Override diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/AssignmentUIState.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/AssignmentUIState.java deleted file mode 100644 index 903f04a960..0000000000 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/AssignmentUIState.java +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright (c) 2019 Bosch Software Innovations 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.ui.management.state; - -import java.io.Serializable; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import org.eclipse.hawkbit.ui.common.entity.DistributionSetIdName; -import org.eclipse.hawkbit.ui.common.entity.TargetIdName; - -/** - * State keeping track of the distribution set assignments that were done in the - * UI. - */ -public class AssignmentUIState implements Serializable, Iterable { - - private static final long serialVersionUID = 1L; - - private final Map> assignments = new HashMap<>(); - - private final Set assignedTargets = new HashSet<>(); - - public Set getAssignedTargets(final DistributionSetIdName distributionSet) { - if (!assignments.containsKey(distributionSet)) { - return Collections.emptySet(); - } - return assignments.get(distributionSet); - } - - public void addAssignment(final DistributionSetIdName distributionSet, final Set targets) { - assignedTargets.addAll(targets); - assignments.computeIfAbsent(distributionSet, key -> new HashSet<>()).addAll(targets); - } - - public void addAssignment(final DistributionSetIdName distributionSet, final TargetIdName target) { - assignedTargets.add(target); - assignments.computeIfAbsent(distributionSet, key -> new HashSet<>()).add(target); - } - - public boolean hasAssignments(final TargetIdName target) { - return assignedTargets.contains(target); - } - - public boolean isAssignedTo(final DistributionSetIdName distributionSet, final TargetIdName target) { - if (!assignments.containsKey(distributionSet)) { - return false; - } - return assignments.get(distributionSet).contains(target); - } - - public boolean isAssigned(final DistributionSetIdName distributionSet) { - return assignments.containsKey(distributionSet); - } - - public boolean isAssigned(final Long distributionSetId) { - return assignments.keySet().stream().anyMatch(key -> distributionSetId.equals(key.getId())); - } - - public void clear() { - assignments.clear(); - assignedTargets.clear(); - } - - @Override - public Iterator iterator() { - return assignments.keySet().iterator(); - } - -} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java index c959a5d92f..e09dadaf0d 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/state/ManagementUIState.java @@ -38,8 +38,6 @@ public class ManagementUIState implements ManagementEntityState, Serializable { private final TargetTableFilters targetTableFilters; - private final AssignmentUIState assignmentState = new AssignmentUIState(); - private final Set deletedDistributionList = new HashSet<>(); private final Set deletedTargetList = new HashSet<>(); @@ -135,10 +133,6 @@ public DistributionTableFilters getDistributionTableFilters() { return distributionTableFilters; } - public AssignmentUIState getAssignmentState() { - return assignmentState; - } - public Set getDeletedDistributionList() { return deletedDistributionList; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java index fb690082e8..0e9997d363 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java @@ -61,7 +61,6 @@ import org.eclipse.hawkbit.ui.management.event.TargetTableEvent.TargetComponentEvent; import org.eclipse.hawkbit.ui.management.miscs.ActionTypeOptionGroupAssignmentLayout; import org.eclipse.hawkbit.ui.management.miscs.MaintenanceWindowLayout; -import org.eclipse.hawkbit.ui.management.state.AssignmentUIState; import org.eclipse.hawkbit.ui.management.state.ManagementUIState; import org.eclipse.hawkbit.ui.management.state.TargetTableFilters; import org.eclipse.hawkbit.ui.push.CancelTargetAssignmentEventContainer; @@ -901,45 +900,37 @@ private Set filterDistributionSetsToAssign(final Set ids) { private void openConfirmationWindowForAssignments(final Target target, final List distributionSets) { - final TargetIdName targetIdName = new TargetIdName(target); - addAssignments(targetIdName, distributionSets); - openConfirmationWindowForAssignment(targetIdName.getTargetName(), - distributionSets.stream().map(DistributionSet::getName).collect(Collectors.toList())); - } - private void openConfirmationWindowForAssignment(final String targetName, final List distributionSetNames) { - final String confirmationMessage; - if (distributionSetNames.size() > 1) { - confirmationMessage = getI18n().getMessage(MESSAGE_ASSIGN_TARGET_TO_MULTIPLE_DISTRIBUTIONS, "target", - targetName, String.join(", ", distributionSetNames)); - } else { - confirmationMessage = getI18n().getMessage(MESSAGE_CONFIRM_ASSIGN_ENTITY, distributionSetNames.get(0), - "target", targetName); - } - confirmDialog = new ConfirmationDialog(getI18n().getMessage(CAPTION_ENTITY_ASSIGN_ACTION_CONFIRMBOX), - confirmationMessage, getI18n().getMessage(UIMessageIdProvider.BUTTON_OK), - getI18n().getMessage(UIMessageIdProvider.BUTTON_CANCEL), ok -> { - if (ok && isMaintenanceWindowValid(maintenanceWindowLayout, getNotification())) { - saveAllAssignments(managementUIState, actionTypeOptionGroupLayout, maintenanceWindowLayout, - deploymentManagement, getNotification(), getEventBus(), getI18n(), this); - } else { - managementUIState.getAssignmentState().clear(); - } - }, createAssignmentTab(actionTypeOptionGroupLayout, maintenanceWindowLayout, saveButtonToggle(), - getI18n(), uiProperties), - UIComponentIdProvider.DIST_SET_TO_TARGET_ASSIGNMENT_CONFIRM_ID); + final String confirmationMessage = getConfirmationMessage(target, distributionSets); + final String caption = getI18n().getMessage(CAPTION_ENTITY_ASSIGN_ACTION_CONFIRMBOX); + final String okLabel = getI18n().getMessage(UIMessageIdProvider.BUTTON_OK); + final String cancelLabel = getI18n().getMessage(UIMessageIdProvider.BUTTON_CANCEL); + + confirmDialog = new ConfirmationDialog(caption, confirmationMessage, okLabel, cancelLabel, ok -> { + if (ok && isMaintenanceWindowValid(maintenanceWindowLayout, getNotification())) { + saveAllAssignments(Collections.singletonList(target), distributionSets, managementUIState, + actionTypeOptionGroupLayout, maintenanceWindowLayout, deploymentManagement, getNotification(), + getEventBus(), getI18n(), this); + } + }, createAssignmentTab(actionTypeOptionGroupLayout, maintenanceWindowLayout, saveButtonToggle(), getI18n(), + uiProperties), UIComponentIdProvider.DIST_SET_TO_TARGET_ASSIGNMENT_CONFIRM_ID); + UI.getCurrent().addWindow(confirmDialog.getWindow()); confirmDialog.getWindow().bringToFront(); } - private Consumer saveButtonToggle() { - return isEnabled -> confirmDialog.getOkButton().setEnabled(isEnabled); + private String getConfirmationMessage(final Target target, final List distributionSets) { + final String targetName = target.getName(); + final int dsCount = distributionSets.size(); + if (dsCount > 1) { + return getI18n().getMessage(MESSAGE_ASSIGN_TARGET_TO_MULTIPLE_DISTRIBUTIONS, dsCount, "target", targetName); + } + return getI18n().getMessage(MESSAGE_CONFIRM_ASSIGN_ENTITY, distributionSets.get(0).getName(), "target", + targetName); } - private void addAssignments(final TargetIdName targetIdName, final List distributionSets) { - final AssignmentUIState assignmentState = managementUIState.getAssignmentState(); - distributionSets.stream().map(DistributionSetIdName::new) - .forEach(distributionSet -> assignmentState.addAssignment(distributionSet, targetIdName)); + private Consumer saveButtonToggle() { + return isEnabled -> confirmDialog.getOkButton().setEnabled(isEnabled); } @Override diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index 78c2a7c3dd..1d3e609b6a 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -708,7 +708,8 @@ message.confirm.delete.entity = Are you sure you want to delete {0} {1}{2}? caption.entity.assign.action.confirmbox = Confirm Assignment message.confirm.assign.entity = Are you sure you want to assign distribution {0} to {1} {2}? message.confirm.assign.multiple.entities = Are you sure you want to assign {0} {1} to distribution {2}? -message.confirm.assign.multiple.entities.multiple.distributions = Are you sure you want to assign {0} {1} to the distributions {2}? +#message.confirm.assign.multiple.entities.multiple.distributions = Are you sure you want to assign {0} {1} to the distributions {2}? +message.confirm.assign.multiple.entities.multiple.distributions = Are you sure you want to assign {0} distributions to {1} {2}? # character descriptions character.whitespace = whitespace From d61ded47ccb494a42da9326d73ec8e7a6e4f441c Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Tue, 14 May 2019 11:55:28 +0200 Subject: [PATCH 46/56] Fix PR review findings Signed-off-by: Stefan Behl --- .../amqp/AmqpMessageDispatcherService.java | 7 ++-- .../amqp/AmqpMessageHandlerService.java | 30 +++++++------- .../amqp/AmqpMessageHandlerServiceTest.java | 8 ++-- .../TenantConfigurationManagement.java | 30 -------------- .../DistributionSetAssignmentResultMap.java | 39 ------------------- .../jpa/JpaDeploymentManagement.java | 9 ++--- .../jpa/JpaTenantConfigurationManagement.java | 7 ---- .../AbstractDistributionSetDetails.java | 4 +- .../DistributionAddUpdateWindowLayout.java | 2 +- 9 files changed, 31 insertions(+), 105 deletions(-) delete mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java index c32e265195..dd226716f5 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java @@ -8,6 +8,8 @@ */ package org.eclipse.hawkbit.amqp; +import static org.eclipse.hawkbit.repository.RepositoryConstants.MAX_ACTION_COUNT; + import java.net.URI; import java.util.Collections; import java.util.HashMap; @@ -77,8 +79,6 @@ public class AmqpMessageDispatcherService extends BaseAmqpService { private static final Logger LOG = LoggerFactory.getLogger(AmqpMessageDispatcherService.class); - private static final int MAX_ACTIONS = RepositoryConstants.MAX_ACTION_COUNT; - private final ArtifactUrlHandler artifactUrlHandler; private final AmqpMessageSenderService amqpSenderService; private final SystemSecurityContext systemSecurityContext; @@ -178,7 +178,8 @@ protected void sendMultiActionRequestMessages(final String tenant, final List { final List activeActions = deploymentManagement - .findActiveActionsByTarget(PageRequest.of(0, MAX_ACTIONS), target.getControllerId()).getContent(); + .findActiveActionsByTarget(PageRequest.of(0, MAX_ACTION_COUNT), target.getControllerId()) + .getContent(); activeActions.forEach(action -> { final DistributionSet distributionSet = action.getDistributionSet(); diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java index 8d3c2bd5d5..9247243ba3 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerService.java @@ -8,6 +8,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.MULTI_ASSIGNMENTS_ENABLED; import java.io.Serializable; @@ -70,8 +71,6 @@ public class AmqpMessageHandlerService extends BaseAmqpService { private static final Logger LOG = LoggerFactory.getLogger(AmqpMessageHandlerService.class); - private static final int MAX_ACTIONS = RepositoryConstants.MAX_ACTION_COUNT; - private final AmqpMessageDispatcherService amqpMessageDispatcherService; private final ControllerManagement controllerManagement; @@ -207,10 +206,10 @@ private void registerTarget(final Message message, final String virtualHost) { final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotexist(thingId, amqpUri); LOG.debug("Target {} reported online state.", thingId); - sendCurrentUpdateCommandToTarget(target); + sendUpdateCommandToTarget(target); } - private void sendCurrentUpdateCommandToTarget(final Target target) { + private void sendUpdateCommandToTarget(final Target target) { if (isMultiAssignmentsEnabled()) { sendCurrentActionsAsMultiActionToTarget(target); } else { @@ -220,7 +219,7 @@ private void sendCurrentUpdateCommandToTarget(final Target target) { private void sendCurrentActionsAsMultiActionToTarget(final Target target) { final List actions = controllerManagement - .findActiveActionsByTarget(PageRequest.of(0, MAX_ACTIONS), target.getControllerId()).getContent(); + .findActiveActionsByTarget(PageRequest.of(0, MAX_ACTION_COUNT), target.getControllerId()).getContent(); final Set distributionSets = actions.stream().map(Action::getDistributionSet) .collect(Collectors.toSet()); @@ -257,8 +256,9 @@ private Map> getSoftwareModulesWith final Map> metadata = controllerManagement .findTargetVisibleMetaDataBySoftwareModuleId(smIds); - return distributionSet.getModules().stream().collect(Collectors.toMap(sm -> sm, - sm -> metadata.containsKey(sm.getId()) ? metadata.get(sm.getId()) : Collections.emptyList())); + return distributionSet.getModules().stream() + .collect(Collectors.toMap(sm -> sm, sm -> metadata.getOrDefault(sm.getId(), Collections.emptyList()))); + } /** @@ -317,12 +317,15 @@ private void updateActionStatus(final Message message) { ? controllerManagement.addCancelActionStatus(actionStatus) : controllerManagement.addUpdateActionStatus(actionStatus); - if (!updatedAction.isActive() - || (updatedAction.hasMaintenanceSchedule() && updatedAction.isMaintenanceWindowAvailable())) { - sendCurrentUpdateCommandToTarget(action.getTarget()); + if (shouldTargetProceed(updatedAction)) { + sendUpdateCommandToTarget(action.getTarget()); } } + private static boolean shouldTargetProceed(final Action action) { + return !action.isActive() || (action.hasMaintenanceSchedule() && action.isMaintenanceWindowAvailable()); + } + private static boolean isCorrelationIdNotEmpty(final Message message) { return StringUtils.hasLength(message.getMessageProperties().getCorrelationId()); } @@ -407,13 +410,12 @@ private static UpdateMode getUpdateMode(final DmfAttributeUpdate update) { } private boolean isMultiAssignmentsEnabled() { - return getConfigValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class, Boolean.FALSE); + return getConfigValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class); } - private T getConfigValue(final String key, final Class valueType, - final T defaultValue) { + private T getConfigValue(final String key, final Class valueType) { return systemSecurityContext - .runAsSystem(() -> tenantConfigurationManagement.getConfigurationValue(key, valueType, defaultValue)); + .runAsSystem(() -> tenantConfigurationManagement.getConfigurationValue(key, valueType).getValue()); } } \ No newline at end of file diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java index 48b79e72a8..c0a14e0e64 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java @@ -139,8 +139,8 @@ public void before() throws Exception { messageConverter = new Jackson2JsonMessageConverter(); when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter); when(artifactManagementMock.findFirstBySHA1(SHA1)).thenReturn(Optional.empty()); - when(tenantConfigurationManagement.getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class, - Boolean.FALSE)).thenReturn(false); + when(tenantConfigurationManagement.getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue()) + .thenReturn(false); final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(); final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware); @@ -474,8 +474,8 @@ public void lookupNextUpdateActionAfterFinished() throws IllegalAccessException final ArgumentCaptor actionPropertiesCaptor = ArgumentCaptor.forClass(ActionProperties.class); final ArgumentCaptor 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"); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TenantConfigurationManagement.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TenantConfigurationManagement.java index ad81fbea8b..7dc73d971f 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TenantConfigurationManagement.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/TenantConfigurationManagement.java @@ -118,36 +118,6 @@ TenantConfigurationValue buildTenantConfigurationVal TenantConfigurationValue getConfigurationValue(String configurationKeyName, Class propertyType); - /** - * Retrieves a configuration value from the e.g. tenant overwritten - * configuration values or in case the tenant does not a have a specific - * configuration the global default value hold in the {@link Environment} or - * the {@code defaultValue} if no global default is set. - * - * @param - * the type of the configuration value - * @param configurationKeyName - * the key of the configuration - * @param propertyType - * the type of the configuration value, e.g. {@code String.class} - * , {@code Integer.class}, etc - * @param defaultValue - * the value to return in case no configuration is found - * @return the converted configuration value either from the tenant specific - * configuration stored or from the fallback default values or - * {@code defaultValue} in case key has not been configured and no - * default value exists - * @throws TenantConfigurationValidatorException - * if the {@code propertyType} and the value in general does not - * match the expected type and format defined by the Key - * @throws ConversionFailedException - * if the property cannot be converted to the given - * {@code propertyType} - */ - @PreAuthorize(value = SpringEvalExpressions.HAS_AUTH_TENANT_CONFIGURATION) - T getConfigurationValue(String configurationKeyName, Class propertyType, - final T defaultValue); - /** * returns the global configuration property either defined in the property * file or an default value otherwise. diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java deleted file mode 100644 index 6372ef6d0f..0000000000 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/DistributionSetAssignmentResultMap.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Copyright (c) 2019 Bosch Software Innovations 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.repository.model; - -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -/** - * Represents a map of {@link DistributionSetAssignmentResultMap} entities. - */ -public class DistributionSetAssignmentResultMap { - - private final Map results = new HashMap<>(); - - public Set distributionSetIds() { - return results.keySet(); - } - - public void putResult(final long dsId, final DistributionSetAssignmentResult result) { - results.put(dsId, result); - } - - public DistributionSetAssignmentResult getResult(final long dsId) { - return results.get(dsId); - } - - public Set> resultSet() { - return results.entrySet(); - } - -} diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index a649728450..f72c75ef23 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -810,17 +810,16 @@ protected ActionRepository getActionRepository() { } protected boolean isActionsAutocloseEnabled() { - return getConfigValue(REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, Boolean.class, Boolean.FALSE); + return getConfigValue(REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, Boolean.class); } private boolean isMultiAssignmentsEnabled() { - return getConfigValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class, Boolean.TRUE); + return getConfigValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class); } - private T getConfigValue(final String key, final Class valueType, - final T defaultValue) { + private T getConfigValue(final String key, final Class valueType) { return systemSecurityContext - .runAsSystem(() -> tenantConfigurationManagement.getConfigurationValue(key, valueType, defaultValue)); + .runAsSystem(() -> tenantConfigurationManagement.getConfigurationValue(key, valueType).getValue()); } } \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java index cafd7045bd..b95e2a03e8 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java @@ -71,13 +71,6 @@ public TenantConfigurationValue getConfigurationValu return buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration); } - @Override - public T getConfigurationValue(final String configurationKeyName, - final Class propertyType, final T defaultValue) { - final TenantConfigurationValue configEntry = getConfigurationValue(configurationKeyName, propertyType); - return configEntry != null ? configEntry.getValue() : defaultValue; - } - /** * Validates the data type of the tenant configuration. If it is possible to * cast to the given data type. diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/AbstractDistributionSetDetails.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/AbstractDistributionSetDetails.java index 2df2e4d528..2dee9c11d2 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/AbstractDistributionSetDetails.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/detailslayout/AbstractDistributionSetDetails.java @@ -192,8 +192,8 @@ private void updateDistributionSetDetailsLayout(final String type, final Boolean } private boolean isMultiAssignmentEnabled() { - return tenantConfigurationManagement.getConfigurationValue(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, - Boolean.class, false); + return tenantConfigurationManagement + .getConfigurationValue(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue(); } private String getMigrationRequiredValue(final Boolean isMigrationRequired) { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java index 1ae4d50013..d76e85f99b 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionAddUpdateWindowLayout.java @@ -295,7 +295,7 @@ public void resetComponents() { private boolean isMultiAssignmentEnabled() { return tenantConfigurationManagement.getConfigurationValue(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, - Boolean.class, false); + Boolean.class).getValue(); } private void populateValuesOfDistribution(final Long editDistId) { From 65b622bbc0769ed3afd8fcdea07172fd654a1bf0 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Tue, 14 May 2019 12:15:11 +0200 Subject: [PATCH 47/56] Fix PR review findings Signed-off-by: Stefan Behl --- .../jpa/JpaDeploymentManagement.java | 3 +- .../jpa/JpaTenantConfigurationManagement.java | 53 +++++++++++++------ .../jpa/OnlineDsAssignmentStrategy.java | 5 +- .../management/dstable/DistributionTable.java | 15 +----- .../dstable/DistributionTableLayout.java | 2 +- 5 files changed, 41 insertions(+), 37 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index f72c75ef23..416c0a53fd 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -506,8 +506,7 @@ private long startScheduledActionsByRolloutGroupParentInNewTransaction(final Lon .filter(Objects::nonNull).collect(Collectors.toList()); if (!targetAssignments.isEmpty()) { - onlineDsAssignmentStrategy.sendDeploymentEvents(distributionSetId, targetAssignments, - isMultiAssignmentsEnabled()); + onlineDsAssignmentStrategy.sendDeploymentEvents(distributionSetId, targetAssignments); } return rolloutGroupActions.getTotalElements(); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java index b95e2a03e8..3a9f57e251 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java @@ -158,23 +158,7 @@ public TenantConfigurationValue addOrUpdateConfigura tenantConfiguration.setValue(value.toString()); } - // prevent the Multi-Assignment feature from being disabled; this code - // path can be removed once we support this case - if (MULTI_ASSIGNMENTS_ENABLED.equals(configurationKeyName) - && !Boolean.parseBoolean(tenantConfiguration.getValue())) { - LOG.debug("The Multi-Assignments '{}' feature cannot be disabled.", configurationKeyName); - throw new TenantConfigurationValueChangeNotAllowedException(); - } - - // prevent the Auto-Close property from being changed if - // Multi-Assignments is enabled - if (REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED.equals(configurationKeyName) - && getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue()) { - LOG.debug( - "The property '{}' must not be changed because the Multi-Assignments feature is currently enabled.", - configurationKeyName); - throw new TenantConfigurationValueChangeNotAllowedException(); - } + assertValueChangeIsAllowed(configurationKeyName, tenantConfiguration); final JpaTenantConfiguration updatedTenantConfiguration = tenantConfigurationRepository .save(tenantConfiguration); @@ -189,6 +173,41 @@ && getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue()) { .value(conversionService.convert(updatedTenantConfiguration.getValue(), clazzT)).build(); } + /** + * Asserts that the requested configuration value change is allowed. Throws + * a {@link TenantConfigurationValueChangeNotAllowedException} otherwise. + * + * @param configurationKeyName + * The configuration key. + * @param tenantConfiguration + * The configuration to be validated. + * + * @throws TenantConfigurationValueChangeNotAllowedException + * if the requested configuration change is not allowed. + */ + private void assertValueChangeIsAllowed(final String key, final JpaTenantConfiguration valueChange) { + assertMultiAssignmentsValueChange(key, valueChange); + assertAutoCloseValueChange(key, valueChange); + } + + @SuppressWarnings("squid:S1172") + private void assertAutoCloseValueChange(final String key, final JpaTenantConfiguration valueChange) { + if (REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED.equals(key) + && getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue()) { + LOG.debug( + "The property '{}' must not be changed because the Multi-Assignments feature is currently enabled.", + key); + throw new TenantConfigurationValueChangeNotAllowedException(); + } + } + + private void assertMultiAssignmentsValueChange(final String key, final JpaTenantConfiguration valueChange) { + if (MULTI_ASSIGNMENTS_ENABLED.equals(key) && !Boolean.parseBoolean(valueChange.getValue())) { + LOG.debug("The Multi-Assignments '{}' feature cannot be disabled.", key); + throw new TenantConfigurationValueChangeNotAllowedException(); + } + } + @Override @CacheEvict(value = "tenantConfiguration", key = "#configurationKeyName") @Transactional diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java index 65af94a680..7c482b0a14 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java @@ -86,9 +86,8 @@ void sendDeploymentEvents(final List assignment } } - void sendDeploymentEvents(final long distributionSetId, final List actions, - final boolean deviceCanProcessMultipleActions) { - if (deviceCanProcessMultipleActions) { + void sendDeploymentEvents(final long distributionSetId, final List actions) { + if (isMultiAssignmentsEnabled()) { sendDeploymentEvent(actions); } else { final List filteredActions = actions.stream().filter(action -> { diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java index ff25f4c579..17644dd47d 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java @@ -30,12 +30,10 @@ import org.eclipse.hawkbit.repository.DistributionSetManagement; import org.eclipse.hawkbit.repository.TargetManagement; import org.eclipse.hawkbit.repository.TargetTagManagement; -import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent; import org.eclipse.hawkbit.repository.model.DistributionSet; import org.eclipse.hawkbit.repository.model.DistributionSetTagAssignmentResult; import org.eclipse.hawkbit.repository.model.Target; -import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties; import org.eclipse.hawkbit.ui.SpPermissionChecker; import org.eclipse.hawkbit.ui.UiProperties; import org.eclipse.hawkbit.ui.common.ConfirmationDialog; @@ -107,8 +105,6 @@ public class DistributionTable extends AbstractNamedVersionTable Date: Tue, 14 May 2019 12:26:11 +0200 Subject: [PATCH 48/56] Fix PR review findings Signed-off-by: Stefan Behl --- .../hawkbit/amqp/AmqpControllerAuthenticationTest.java | 6 ------ hawkbit-ui/src/main/resources/messages.properties | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java index f4054d375d..386480ae56 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpControllerAuthenticationTest.java @@ -16,13 +16,10 @@ 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.DbArtifactHash; import org.eclipse.hawkbit.cache.DownloadIdCache; import org.eclipse.hawkbit.dmf.amqp.api.MessageHeaderKey; import org.eclipse.hawkbit.dmf.amqp.api.MessageType; @@ -161,9 +158,6 @@ public void before() throws Exception { when(artifactManagementMock.get(ARTIFACT_ID)).thenReturn(Optional.of(testArtifact)); when(artifactManagementMock.findFirstBySHA1(SHA1)).thenReturn(Optional.of(testArtifact)); - 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(), systemSecurityContext, tenantConfigurationManagementMock); diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index 1d3e609b6a..7d976e005e 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -70,6 +70,7 @@ header.dist.twintable.available=Available header.target.installed = Installed header.target.assigned = Assigned header.type=Type +header.migrations.step=IsRequiredMigrationStep header.action=Actions header.action.run=Run header.action.approve=Approve From a308a9632b48da21ba1244422318f4a03927da59 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Tue, 14 May 2019 13:39:14 +0200 Subject: [PATCH 49/56] Fix PR review findings, Sonar issues, and test failures Signed-off-by: Stefan Behl --- .../amqp/AmqpMessageDispatcherService.java | 14 +++--- .../amqp/AmqpMessageHandlerServiceTest.java | 9 +++- ...ssageDispatcherServiceIntegrationTest.java | 36 +++++++--------- .../event/remote/DeploymentEvent.java | 5 +-- .../event/remote/MultiActionEvent.java | 43 +++++++++++++++++++ .../model/TenantConfigurationValue.java | 4 +- .../org/eclipse/hawkbit/event/EventType.java | 6 +-- .../jpa/OnlineDsAssignmentStrategy.java | 19 +++++--- .../jpa/DeploymentManagementTest.java | 4 +- .../management/dstable/DistributionTable.java | 9 ---- 10 files changed, 94 insertions(+), 55 deletions(-) create mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionEvent.java diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java index dd226716f5..6ed6a10845 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java @@ -39,7 +39,7 @@ import org.eclipse.hawkbit.repository.SoftwareModuleManagement; import org.eclipse.hawkbit.repository.SystemManagement; import org.eclipse.hawkbit.repository.TargetManagement; -import org.eclipse.hawkbit.repository.event.remote.DeploymentEvent; +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; @@ -156,19 +156,17 @@ protected void targetAssignDistributionSet(final TargetAssignDistributionSetEven } /** - * Method to send a message to a RabbitMQ exchange after the Distribution - * set has been assign to a Target. + * Listener for Multi-Action events. * * @param e - * the object to be send. + * the Multi-Action event to be processed */ - @EventListener(classes = DeploymentEvent.class) - protected void onDeployment(final DeploymentEvent e) { + @EventListener(classes = MultiActionEvent.class) + protected void onMultiAction(final MultiActionEvent e) { if (isNotFromSelf(e)) { return; } - - LOG.debug("DeploymentEvent received. I will forward it to DMF broker."); + LOG.debug("MultiActionEvent received for {}", e.getControllerIds()); sendMultiActionRequestMessages(e.getTenant(), e.getControllerIds()); } diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java index c0a14e0e64..9fb1ecc494 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java @@ -51,6 +51,7 @@ 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; @@ -134,13 +135,17 @@ public class AmqpMessageHandlerServiceTest { @Captor private ArgumentCaptor modeCaptor; + @Mock + private TenantConfigurationValue multiAssignmentConfig; + @Before public void before() throws Exception { messageConverter = new Jackson2JsonMessageConverter(); when(rabbitTemplate.getMessageConverter()).thenReturn(messageConverter); when(artifactManagementMock.findFirstBySHA1(SHA1)).thenReturn(Optional.empty()); - when(tenantConfigurationManagement.getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue()) - .thenReturn(false); + when(tenantConfigurationManagement.getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class)) + .thenReturn(multiAssignmentConfig); + when(multiAssignmentConfig.getValue()).thenReturn(false); final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(); final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware); diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java index 9f4bf8e99b..9512112483 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java @@ -26,7 +26,7 @@ import org.eclipse.hawkbit.dmf.amqp.api.EventTopic; import org.eclipse.hawkbit.dmf.json.model.DmfActionStatus; -import org.eclipse.hawkbit.repository.event.remote.DeploymentEvent; +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; @@ -156,7 +156,7 @@ public void assignDistributionSetMultipleTimes() { @Test @Description("If multi assignment is enabled multi-action messages are sent.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), - @Expect(type = DeploymentEvent.class, count = 2), + @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), @@ -182,7 +182,7 @@ public void assignMultipleDsInMultiAssignMode() { @Test @Description("Handle cancelation process of an action in multi assignment mode.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), - @Expect(type = DeploymentEvent.class, count = 3), + @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), @@ -213,7 +213,7 @@ public void cancelActionInMultiAssignMode() { @Test @Description("Handle finishing an action in multi assignment mode.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), - @Expect(type = DeploymentEvent.class, count = 2), + @Expect(type = MultiActionEvent.class, count = 2), @Expect(type = TargetAttributesRequestedEvent.class, count = 1), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0), @Expect(type = CancelTargetAssignmentEvent.class, count = 0), @@ -232,8 +232,7 @@ public void finishActionInMultiAssignMode() { waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION, EventTopic.MULTI_ACTION); updateActionViaDmfClient(controllerId, actionId1, DmfActionStatus.FINISHED); - waitUntilEventMessagesAreDispatchedToTarget(EventTopic.REQUEST_ATTRIBUTES_UPDATE, - EventTopic.MULTI_ACTION); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.REQUEST_ATTRIBUTES_UPDATE, EventTopic.MULTI_ACTION); assertRequestAttributesUpdateMessage(controllerId); assertLatestMultiActionMessage(controllerId, Arrays.asList(action2Install)); } @@ -241,7 +240,7 @@ public void finishActionInMultiAssignMode() { @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 = DeploymentEvent.class, count = 2), + @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), @@ -258,7 +257,7 @@ public void assignDsMultipleTimesInMultiAssignMode() { waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION); final Long actionId2 = assignDistributionSet(ds.getId(), controllerId).getActionIds().get(0); waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION); - + final Entry action1Install = new SimpleEntry<>(actionId1, EventTopic.DOWNLOAD_AND_INSTALL); final Entry action2Install = new SimpleEntry<>(actionId2, EventTopic.DOWNLOAD_AND_INSTALL); assertLatestMultiActionMessage(controllerId, Arrays.asList(action1Install, action2Install)); @@ -279,7 +278,7 @@ private Long assignNewDsToTarget(final String controllerId) { @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 = DeploymentEvent.class, count = 2), + @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), @@ -310,14 +309,13 @@ public void startRolloutsWithSameDsInMultiAssignMode() { @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 = DeploymentEvent.class, count = 3), - @Expect(type = ActionCreatedEvent.class, count = 3), @Expect(type = ActionUpdatedEvent.class, count = 5), + @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 = 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() { @@ -337,19 +335,17 @@ public void startMultipleRolloutsAndFinishInMultiAssignMode() { createAndStartRollout(ds1, filterQuery); waitUntilEventMessagesAreDispatchedToTarget(EventTopic.MULTI_ACTION); assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds1, smIds2, smIds1)); - + final List 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); + 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); + waitUntilEventMessagesAreDispatchedToTarget(EventTopic.REQUEST_ATTRIBUTES_UPDATE, EventTopic.MULTI_ACTION); assertLatestMultiActionMessageContainsInstallMessages(controllerId, Arrays.asList(smIds1)); } @@ -397,7 +393,6 @@ public void sendDeleteMessage() { assertDeleteMessage(controllerId); } - @Test @Description("Verify that attribute update is requested after device successfully closed software update.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @@ -466,7 +461,6 @@ private void waitUntil(final Callable callable) { } private void enableMultiAssignments() { - tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, - true); + tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, true); } } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java index 9edf0395b2..7b1ac14ada 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java @@ -16,7 +16,7 @@ * TenantAwareEvent that gets sent when a distribution set gets assigned to a * target. */ -public class DeploymentEvent extends RemoteTenantAwareEvent implements Iterable { +public abstract class DeploymentEvent extends RemoteTenantAwareEvent implements Iterable { private static final long serialVersionUID = 1L; @@ -39,8 +39,7 @@ public DeploymentEvent() { * @param controllerIds * the controller IDs */ - public DeploymentEvent(final String tenant, final String applicationId, - final List controllerIds) { + public DeploymentEvent(final String tenant, final String applicationId, final List controllerIds) { super(applicationId, tenant, applicationId); this.controllerIds.addAll(controllerIds); } diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionEvent.java new file mode 100644 index 0000000000..3a61f57246 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionEvent.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2015 Bosch Software Innovations 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.repository.event.remote; + +import java.util.List; + +/** + * Deployment event which implies that the Multi-Assignment feature is enabled. + */ +@SuppressWarnings("squid:MaximumInheritanceDepth") +public class MultiActionEvent extends DeploymentEvent { + + private static final long serialVersionUID = 1L; + + /** + * Default constructor for serialization purposes. + */ + public MultiActionEvent() { + super(); + } + + /** + * Constructs a Multi-Action event for the targets with the given controller + * IDs. + * + * @param tenant + * this event is scoped to + * @param applicationId + * of the application + * @param controllerIds + * of the targets this event refers to + */ + public MultiActionEvent(final String tenant, final String applicationId, final List controllerIds) { + super(tenant, applicationId, controllerIds); + } + +} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TenantConfigurationValue.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TenantConfigurationValue.java index 5f27357cec..933a9dd428 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TenantConfigurationValue.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TenantConfigurationValue.java @@ -11,12 +11,12 @@ import java.io.Serializable; /** - * represents a tenant configuration value including some meta data + * Represents a tenant configuration value including some meta data * * @param * type of the configuration value */ -public final class TenantConfigurationValue implements Serializable { +public class TenantConfigurationValue implements Serializable { private static final long serialVersionUID = 1L; private T value; diff --git a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventType.java b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventType.java index 4c0f5951c6..49fba4bcb8 100644 --- a/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventType.java +++ b/hawkbit-repository/hawkbit-repository-core/src/main/java/org/eclipse/hawkbit/event/EventType.java @@ -13,11 +13,11 @@ import java.util.Map.Entry; import java.util.Optional; -import org.eclipse.hawkbit.repository.event.remote.DeploymentEvent; import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.DistributionSetTagDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.DistributionSetTypeDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent; +import org.eclipse.hawkbit.repository.event.remote.MultiActionEvent; import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.RolloutGroupDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleDeletedEvent; @@ -134,8 +134,8 @@ public class EventType { // target attributes requested flag TYPES.put(37, TargetAttributesRequestedEvent.class); - // generic deployment event for assignments and cancellations - TYPES.put(38, DeploymentEvent.class); + // deployment event for assignments and /or cancellations + TYPES.put(38, MultiActionEvent.class); } private int value; diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java index 7c482b0a14..525f522585 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java @@ -18,7 +18,7 @@ import java.util.stream.Collectors; import org.eclipse.hawkbit.repository.QuotaManagement; -import org.eclipse.hawkbit.repository.event.remote.DeploymentEvent; +import org.eclipse.hawkbit.repository.event.remote.MultiActionEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.jpa.configuration.Constants; import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor; @@ -64,7 +64,7 @@ void sendTargetUpdatedEvents(final DistributionSet set, final List ta } void sendDeploymentEvent(final Target target) { - sendDeploymentEvent(target.getTenant(), Collections.singletonList(target.getControllerId())); + sendMultiActionEvent(target.getTenant(), Collections.singletonList(target.getControllerId())); } @Override @@ -111,7 +111,7 @@ private void sendDeploymentEvent(final List actions) { return; } final String tenant = filteredActions.get(0).getTenant(); - sendDeploymentEvent(tenant, filteredActions.stream().map(action -> action.getTarget().getControllerId()) + sendMultiActionEvent(tenant, filteredActions.stream().map(action -> action.getTarget().getControllerId()) .collect(Collectors.toList())); } @@ -186,9 +186,18 @@ private void sendTargetAssignDistributionSetEvent(final String tenant, final lon distributionSetId, actions, bus.getId(), actions.get(0).isMaintenanceWindowAvailable()))); } - private void sendDeploymentEvent(final String tenant, final List controllerIds) { + /** + * Helper to fire a {@link MultiActionEvent}. This method may only be called + * if the Multi-Assignments feature is enabled. + * + * @param tenant + * the event is scoped to + * @param controllerIds + * of the targets the event refers to + */ + private void sendMultiActionEvent(final String tenant, final List controllerIds) { afterCommit.afterCommit( - () -> eventPublisher.publishEvent(new DeploymentEvent(tenant, bus.getId(), controllerIds))); + () -> eventPublisher.publishEvent(new MultiActionEvent(tenant, bus.getId(), controllerIds))); } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java index bb588629c2..f07ad0d8eb 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java @@ -25,7 +25,7 @@ import java.util.stream.IntStream; import org.eclipse.hawkbit.repository.ActionStatusFields; -import org.eclipse.hawkbit.repository.event.remote.DeploymentEvent; +import org.eclipse.hawkbit.repository.event.remote.MultiActionEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; @@ -540,7 +540,7 @@ public void assignDistributionSetAndAutoCloseActiveActions() { @Expect(type = TargetUpdatedEvent.class, count = 20), @Expect(type = ActionCreatedEvent.class, count = 20), @Expect(type = DistributionSetCreatedEvent.class, count = 2), @Expect(type = SoftwareModuleCreatedEvent.class, count = 6), - @Expect(type = DeploymentEvent.class, count = 2), + @Expect(type = MultiActionEvent.class, count = 2), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0) }) public void previousAssignmentsAreNotCanceledInMultiAssignMode() { setMultiAssignmentsEnabled(true); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java index 17644dd47d..83ab708a4b 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/dstable/DistributionTable.java @@ -493,15 +493,6 @@ private boolean isNoTagButton(final String tagData, final String targetNoTagData return false; } - private String getPendingActionMessage(final String message, final String controllerId, - final String distNameVersion) { - String pendActionMsg = getI18n().getMessage("message.target.assigned.pending"); - if (null == message) { - pendActionMsg = getI18n().getMessage("message.dist.pending.action", controllerId, distNameVersion); - } - return pendActionMsg; - } - private void updateDistributionInTable(final DistributionSet editedDs) { final Item item = getContainerDataSource().getItem(editedDs.getId()); if (item == null) { From a0c699af936081ee650426b675c2e5a61028f904 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Tue, 14 May 2019 13:52:12 +0200 Subject: [PATCH 50/56] Fix PR review findings Signed-off-by: Stefan Behl --- .../amqp/AmqpMessageDispatcherService.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java index 6ed6a10845..c5ffd53ee6 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/main/java/org/eclipse/hawkbit/amqp/AmqpMessageDispatcherService.java @@ -173,23 +173,24 @@ protected void onMultiAction(final MultiActionEvent e) { protected void sendMultiActionRequestMessages(final String tenant, final List controllerIds) { final Map>> softwareModuleMetadata = new HashMap<>(); - targetManagement.getByControllerID(controllerIds).stream().forEach(target -> { + targetManagement.getByControllerID(controllerIds).stream() + .filter(target -> IpUtil.isAmqpUri(target.getAddress())).forEach(target -> { - final List activeActions = deploymentManagement - .findActiveActionsByTarget(PageRequest.of(0, MAX_ACTION_COUNT), target.getControllerId()) - .getContent(); + final List activeActions = deploymentManagement + .findActiveActionsByTarget(PageRequest.of(0, MAX_ACTION_COUNT), target.getControllerId()) + .getContent(); - activeActions.forEach(action -> { - final DistributionSet distributionSet = action.getDistributionSet(); - softwareModuleMetadata.computeIfAbsent(distributionSet.getId(), - id -> getSoftwareModulesWithMetadata(distributionSet)); - }); + activeActions.forEach(action -> { + final DistributionSet distributionSet = action.getDistributionSet(); + softwareModuleMetadata.computeIfAbsent(distributionSet.getId(), + id -> getSoftwareModulesWithMetadata(distributionSet)); + }); - if (!activeActions.isEmpty()) { - sendMultiActionRequestToTarget(tenant, target, activeActions, softwareModuleMetadata); - } + if (!activeActions.isEmpty()) { + sendMultiActionRequestToTarget(tenant, target, activeActions, softwareModuleMetadata); + } - }); + }); } From 7200bedd4c46fb26130d9af9e089b9213c2dfeed Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Tue, 14 May 2019 14:08:57 +0200 Subject: [PATCH 51/56] Fix PR review findings Signed-off-by: Stefan Behl --- .../AbstractAmqpServiceIntegrationTest.java | 18 +----------------- .../jpa/JpaDeploymentManagement.java | 19 +++++++------------ .../jpa/JpaTenantConfigurationManagement.java | 2 +- .../MgmtTenantManagementResourceTest.java | 4 ++-- 4 files changed, 11 insertions(+), 32 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java index a8784d36ba..375136e564 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java @@ -117,16 +117,6 @@ protected void waitUntilEventMessagesAreDispatchedToTarget(final EventTopic... e replyToListener.resetLatestEventMessageTopics(); } - protected void purgeReplyToListener() { - replyToListener.purge(); - } - - protected void verifyDeadLetterMessages(final int expectedMessages) { - createConditionFactory().untilAsserted(() -> { - Mockito.verify(getDeadletterListener(), Mockito.times(expectedMessages)).handleMessage(Mockito.any()); - }); - } - protected DeadletterListener getDeadletterListener() { return deadletterListener; } @@ -243,11 +233,6 @@ private static List getDownloadAndUpdateRequests(fi .map(multiAction -> (DmfDownloadAndUpdateRequest) multiAction.getAction()).collect(Collectors.toList()); } - private static List getNonDownloadAndUpdateRequests(final DmfMultiActionRequest request) { - return request.getElements().stream().filter(multiaction -> !isDownloadAndUpdateRequest(multiaction)) - .collect(Collectors.toList()); - } - private static boolean isDownloadAndUpdateRequest(final DmfMultiActionElement multiActionElement) { return multiActionElement.getTopic().equals(EventTopic.DOWNLOAD) || multiActionElement.getTopic().equals(EventTopic.DOWNLOAD_AND_INSTALL); @@ -259,8 +244,7 @@ protected void assertLatestMultiActionMessage(final String controllerId, assertThat(actionsFromMessage).containsExactlyInAnyOrderElementsOf(actionsExpected); } - protected List> getLatestMultiActionMessageActions( - final String expectedControllerId) { + protected List> getLatestMultiActionMessageActions(final String expectedControllerId) { final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION); assertThat(multiactionMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID)) .isEqualTo(expectedControllerId); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index 416c0a53fd..5b8fd9b94f 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -70,7 +70,6 @@ import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.tenancy.TenantAware; -import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.bus.BusProperties; @@ -409,7 +408,6 @@ private void detachEntitiesAndSendTargetUpdatedEvents(final JpaDistributionSet s entityManager.detach(set); // detaching as the entity has been updated by the JPQL query above targets.forEach(entityManager::detach); - // send out TargetUpdated events assignmentStrategy.sendTargetUpdatedEvents(set, targets); } @@ -553,19 +551,16 @@ private JpaAction startScheduledActionIfNoCancelationHasToBeHandledFirst(final J // check if we need to override running update actions final List overrideObsoleteUpdateActions; - if (!isMultiAssignmentsEnabled()) { - if (systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement - .getConfigurationValue(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, Boolean.class) - .getValue())) { + if (isMultiAssignmentsEnabled()) { + overrideObsoleteUpdateActions = Collections.emptyList(); + } else { + final List targetId = Collections.singletonList(action.getTarget().getId()); + if (isActionsAutocloseEnabled()) { overrideObsoleteUpdateActions = Collections.emptyList(); - onlineDsAssignmentStrategy - .closeObsoleteUpdateActions(Collections.singletonList(action.getTarget().getId())); + onlineDsAssignmentStrategy.closeObsoleteUpdateActions(targetId); } else { - overrideObsoleteUpdateActions = onlineDsAssignmentStrategy - .overrideObsoleteUpdateActions(Collections.singletonList(action.getTarget().getId())); + overrideObsoleteUpdateActions = onlineDsAssignmentStrategy.overrideObsoleteUpdateActions(targetId); } - } else { - overrideObsoleteUpdateActions = Collections.emptyList(); } action.setActive(true); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java index 3a9f57e251..22eec469d2 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaTenantConfigurationManagement.java @@ -201,7 +201,7 @@ && getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue()) { } } - private void assertMultiAssignmentsValueChange(final String key, final JpaTenantConfiguration valueChange) { + private static void assertMultiAssignmentsValueChange(final String key, final JpaTenantConfiguration valueChange) { if (MULTI_ASSIGNMENTS_ENABLED.equals(key) && !Boolean.parseBoolean(valueChange.getValue())) { LOG.debug("The Multi-Assignments '{}' feature cannot be disabled.", key); throw new TenantConfigurationValueChangeNotAllowedException(); diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java index 42dec09f70..b369b827aa 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java @@ -30,8 +30,8 @@ public class MgmtTenantManagementResourceTest extends AbstractManagementApiIntegrationTest { @Test - @Description("Multiassignment can not be deactivated.") - public void deactivateMultiassignment() throws Exception { + @Description("The 'multi.assignments.enabled' property must not be changed to false.") + public void deactivateMultiAssignment() throws Exception { final String multiAssignmentKey = "multi.assignments.enabled"; final String bodyActivate = new JSONObject().put("value", true).toString(); final String bodyDeactivate = new JSONObject().put("value", false).toString(); From f691f5c0b2091a6078f71aa8dafc7bbcd38d79fb Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Tue, 14 May 2019 14:45:06 +0200 Subject: [PATCH 52/56] Fix PR review findings Signed-off-by: Stefan Behl --- .../jpa/AbstractDsAssignmentStrategy.java | 18 ++---------------- .../jpa/JpaDeploymentManagement.java | 9 ++++----- .../jpa/OfflineDsAssignmentStrategy.java | 6 ++---- .../jpa/OnlineDsAssignmentStrategy.java | 14 +++++++++++++- 4 files changed, 21 insertions(+), 26 deletions(-) diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java index c3fe7d61a2..4bac7849a8 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/AbstractDsAssignmentStrategy.java @@ -13,7 +13,6 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; import org.eclipse.hawkbit.repository.QuotaManagement; @@ -51,17 +50,14 @@ public abstract class AbstractDsAssignmentStrategy { protected final AfterTransactionCommitExecutor afterCommit; protected final ApplicationEventPublisher eventPublisher; protected final BusProperties bus; - private final ActionRepository actionRepository; + protected final ActionRepository actionRepository; private final ActionStatusRepository actionStatusRepository; private final QuotaManagement quotaManagement; - private final Supplier multiAssignmentsConfig; - AbstractDsAssignmentStrategy(final TargetRepository targetRepository, final AfterTransactionCommitExecutor afterCommit, final ApplicationEventPublisher eventPublisher, final BusProperties bus, final ActionRepository actionRepository, - final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement, - final Supplier multiAssignmentsConfig) { + final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement) { this.targetRepository = targetRepository; this.afterCommit = afterCommit; this.eventPublisher = eventPublisher; @@ -69,7 +65,6 @@ public abstract class AbstractDsAssignmentStrategy { this.actionRepository = actionRepository; this.actionStatusRepository = actionStatusRepository; this.quotaManagement = quotaManagement; - this.multiAssignmentsConfig = multiAssignmentsConfig; } /** @@ -242,15 +237,6 @@ JpaActionStatus createActionStatus(final JpaAction action, final String actionMe return actionStatus; } - boolean hasPendingCancellations(final Target target) { - return actionRepository.findByActiveAndTarget(null, target.getControllerId(), true).getContent().stream() - .anyMatch(action -> action.getStatus() == Status.CANCELING); - } - - boolean isMultiAssignmentsEnabled() { - return multiAssignmentsConfig.get(); - } - private void assertActionsPerTargetQuota(final Target target, final int requested) { final int quota = quotaManagement.getMaxActionsPerTarget(); QuotaHelper.assertAssignmentQuota(target.getId(), requested, quota, Action.class, Target.class, diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index 5b8fd9b94f..9b8aca879b 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -155,7 +155,7 @@ protected JpaDeploymentManagement(final EntityManager entityManager, final Actio onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, afterCommit, eventPublisher, bus, actionRepository, actionStatusRepository, quotaManagement, this::isMultiAssignmentsEnabled); offlineDsAssignmentStrategy = new OfflineDsAssignmentStrategy(targetRepository, afterCommit, eventPublisher, - bus, actionRepository, actionStatusRepository, quotaManagement, this::isMultiAssignmentsEnabled); + bus, actionRepository, actionStatusRepository, quotaManagement); this.tenantConfigurationManagement = tenantConfigurationManagement; this.quotaManagement = quotaManagement; this.systemSecurityContext = systemSecurityContext; @@ -350,19 +350,18 @@ private void assertMaxTargetsPerManualAssignmentQuota(final Long distributionSet quotaManagement.getMaxTargetsPerManualAssignment(), Target.class, DistributionSet.class, null); } - private Set closeOrCancelActiveActions(final AbstractDsAssignmentStrategy assignmentStrategy, + private void closeOrCancelActiveActions(final AbstractDsAssignmentStrategy assignmentStrategy, final List> targetIdsChunks) { if (isMultiAssignmentsEnabled()) { LOG.debug("Multi Assignments feature is enabled: No need to close /cancel active actions."); - return Collections.emptySet(); + return; } if (isActionsAutocloseEnabled()) { assignmentStrategy.closeActiveActions(targetIdsChunks); - return Collections.emptySet(); } else { - return assignmentStrategy.cancelActiveActions(targetIdsChunks); + assignmentStrategy.cancelActiveActions(targetIdsChunks); } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java index 0bd91f2e89..5bcdef8dd8 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OfflineDsAssignmentStrategy.java @@ -13,7 +13,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; import org.eclipse.hawkbit.repository.QuotaManagement; @@ -46,10 +45,9 @@ public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { OfflineDsAssignmentStrategy(final TargetRepository targetRepository, final AfterTransactionCommitExecutor afterCommit, final ApplicationEventPublisher eventPublisher, final BusProperties bus, final ActionRepository actionRepository, - final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement, - final Supplier multiAssignmentsConfig) { + final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement) { super(targetRepository, afterCommit, eventPublisher, bus, actionRepository, actionStatusRepository, - quotaManagement, multiAssignmentsConfig); + quotaManagement); } @Override diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java index 525f522585..2b5c536ac5 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java @@ -46,13 +46,16 @@ */ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { + private final Supplier multiAssignmentsConfig; + OnlineDsAssignmentStrategy(final TargetRepository targetRepository, final AfterTransactionCommitExecutor afterCommit, final ApplicationEventPublisher eventPublisher, final BusProperties bus, final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository, final QuotaManagement quotaManagement, final Supplier multiAssignmentsConfig) { super(targetRepository, afterCommit, eventPublisher, bus, actionRepository, actionStatusRepository, - quotaManagement, multiAssignmentsConfig); + quotaManagement); + this.multiAssignmentsConfig = multiAssignmentsConfig; } @Override @@ -186,6 +189,11 @@ private void sendTargetAssignDistributionSetEvent(final String tenant, final lon distributionSetId, actions, bus.getId(), actions.get(0).isMaintenanceWindowAvailable()))); } + private boolean hasPendingCancellations(final Target target) { + return actionRepository.findByActiveAndTarget(null, target.getControllerId(), true).getContent().stream() + .anyMatch(action -> action.getStatus() == Status.CANCELING); + } + /** * Helper to fire a {@link MultiActionEvent}. This method may only be called * if the Multi-Assignments feature is enabled. @@ -200,4 +208,8 @@ private void sendMultiActionEvent(final String tenant, final List contro () -> eventPublisher.publishEvent(new MultiActionEvent(tenant, bus.getId(), controllerIds))); } + private boolean isMultiAssignmentsEnabled() { + return multiAssignmentsConfig.get(); + } + } From 3528b749c1b9360bbf9a35c8f0bf677f08c1ee8c Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Tue, 14 May 2019 15:02:30 +0200 Subject: [PATCH 53/56] Fix Sonar findings Signed-off-by: Stefan Behl --- .../hawkbit/amqp/AmqpMessageHandlerServiceTest.java | 7 +++---- .../hawkbit/repository/model/TenantConfigurationValue.java | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java index 9fb1ecc494..0b6469efdb 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/amqp/AmqpMessageHandlerServiceTest.java @@ -135,17 +135,16 @@ public class AmqpMessageHandlerServiceTest { @Captor private ArgumentCaptor modeCaptor; - @Mock - private TenantConfigurationValue multiAssignmentConfig; - @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); - when(multiAssignmentConfig.getValue()).thenReturn(false); final SecurityContextTenantAware tenantAware = new SecurityContextTenantAware(); final SystemSecurityContext systemSecurityContext = new SystemSecurityContext(tenantAware); diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TenantConfigurationValue.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TenantConfigurationValue.java index 933a9dd428..dbb242feca 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TenantConfigurationValue.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TenantConfigurationValue.java @@ -16,7 +16,7 @@ * @param * type of the configuration value */ -public class TenantConfigurationValue implements Serializable { +public final class TenantConfigurationValue implements Serializable { private static final long serialVersionUID = 1L; private T value; From 070bafb479132e264c471efa4d42c055aca2f356 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Tue, 14 May 2019 15:32:41 +0200 Subject: [PATCH 54/56] Fix PR review findings Signed-off-by: Stefan Behl --- .../AbstractAmqpServiceIntegrationTest.java | 38 +++---------------- ...ssageDispatcherServiceIntegrationTest.java | 34 +++++++++++++++++ .../integration/listener/ReplyToListener.java | 10 ++--- 3 files changed, 45 insertions(+), 37 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java index 375136e564..ffccf333cd 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java @@ -12,6 +12,7 @@ import java.nio.charset.StandardCharsets; import java.util.AbstractMap.SimpleEntry; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -34,7 +35,6 @@ import org.eclipse.hawkbit.dmf.json.model.DmfMetadata; 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.integration.listener.DeadletterListener; import org.eclipse.hawkbit.integration.listener.ReplyToListener; import org.eclipse.hawkbit.matcher.SoftwareModuleJsonMatcher; @@ -76,8 +76,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 @@ -93,12 +93,12 @@ public void initListener() { Mockito.reset(deadletterListener); replyToListener = harness.getSpy(ReplyToListener.LISTENER_ID); assertThat(replyToListener).isNotNull(); - Mockito.reset(replyToListener); replyToListener.purge(); + Mockito.reset(replyToListener); getDmfClient().setExchange(AmqpSettings.DMF_EXCHANGE); } - protected T waitUntilIsPresent(final Callable> callable) { + private T waitUntilIsPresent(final Callable> callable) { createConditionFactory().until(() -> { return securityRule.runAsPrivileged(() -> callable.call().isPresent()); }); @@ -112,7 +112,8 @@ protected T waitUntilIsPresent(final Callable> callable) { protected void waitUntilEventMessagesAreDispatchedToTarget(final EventTopic... eventTopics) { createConditionFactory().untilAsserted(() -> { - assertThat(eventTopics).containsExactlyInAnyOrderElementsOf(replyToListener.getLatestEventMessageTopics()); + assertThat(replyToListener.getLatestEventMessageTopics()) + .containsExactlyInAnyOrderElementsOf(Arrays.asList(eventTopics)); }); replyToListener.resetLatestEventMessageTopics(); } @@ -211,33 +212,6 @@ protected void assertDownloadAndInstallMessage(final Set softwar } - protected void assertLatestMultiActionMessageContainsInstallMessages(final String controllerId, - final List> 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> smIdsOfActionsFound = getDownloadAndUpdateRequests(multiActionRequest).stream() - .map(AbstractAmqpServiceIntegrationTest::getSmIds).collect(Collectors.toList()); - assertThat(smIdsOfActionsFound).containsExactlyElementsOf(smIdsOfActionsExpected); - } - - private static Set getSmIds(final DmfDownloadAndUpdateRequest request) { - return request.getSoftwareModules().stream().map(DmfSoftwareModule::getModuleId).collect(Collectors.toSet()); - } - - private static List getDownloadAndUpdateRequests(final DmfMultiActionRequest request) { - return request.getElements().stream().filter(AbstractAmqpServiceIntegrationTest::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); - } - protected void assertLatestMultiActionMessage(final String controllerId, final List> actionsExpected) { final List> actionsFromMessage = getLatestMultiActionMessageActions(controllerId); diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java index 9512112483..b15c9e0400 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java @@ -25,7 +25,12 @@ 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; @@ -463,4 +468,33 @@ private void waitUntil(final Callable callable) { private void enableMultiAssignments() { tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, true); } + + private void assertLatestMultiActionMessageContainsInstallMessages(final String controllerId, + final List> 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> smIdsOfActionsFound = getDownloadAndUpdateRequests(multiActionRequest).stream() + .map(AmqpMessageDispatcherServiceIntegrationTest::getSmIds).collect(Collectors.toList()); + assertThat(smIdsOfActionsFound).containsExactlyInAnyOrderElementsOf(smIdsOfActionsExpected); + } + + private static Set getSmIds(final DmfDownloadAndUpdateRequest request) { + return request.getSoftwareModules().stream().map(DmfSoftwareModule::getModuleId).collect(Collectors.toSet()); + } + + private static List 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); + } + } diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java index 172809edb7..a16bcabe40 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/listener/ReplyToListener.java @@ -30,7 +30,7 @@ public class ReplyToListener implements TestRabbitListener { public static final String REPLY_TO_QUEUE = "reply_queue"; private final Map> eventMessages = new EnumMap<>(EventTopic.class); - private final List latestEventMessageTopics = new ArrayList<>(); + private final List eventMessageTopics = new ArrayList<>(); private final Map deleteMessages = new HashMap<>(); private final Map pingResponseMessages = new HashMap<>(); @@ -43,7 +43,7 @@ public void handleMessage(final Message message) { if (messageType == MessageType.EVENT) { final EventTopic eventTopic = EventTopic .valueOf(message.getMessageProperties().getHeaders().get(MessageHeaderKey.TOPIC).toString()); - latestEventMessageTopics.add(eventTopic); + eventMessageTopics.add(eventTopic); eventMessages.merge(eventTopic, Collections.singletonList(message), (oldList, listToAdd) -> { final List newList = new ArrayList<>(oldList); newList.addAll(listToAdd); @@ -74,15 +74,15 @@ public void purge() { eventMessages.clear(); deleteMessages.clear(); pingResponseMessages.clear(); - latestEventMessageTopics.clear(); + eventMessageTopics.clear(); } public List getLatestEventMessageTopics() { - return latestEventMessageTopics; + return eventMessageTopics; } public void resetLatestEventMessageTopics() { - latestEventMessageTopics.clear(); + eventMessageTopics.clear(); } public Message getLatestEventMessage(final EventTopic eventTopic) { From f61da79c82595c675707b249eda34da837bf5a74 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Wed, 15 May 2019 10:23:30 +0200 Subject: [PATCH 55/56] Fix PR review findings Signed-off-by: Stefan Behl --- .../event/remote/DeploymentEvent.java | 56 ------------------- .../event/remote/MultiActionEvent.java | 41 +++++++++----- .../jpa/JpaDeploymentManagement.java | 6 +- .../jpa/OnlineDsAssignmentStrategy.java | 42 ++++++++------ 4 files changed, 54 insertions(+), 91 deletions(-) delete mode 100644 hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java deleted file mode 100644 index 7b1ac14ada..0000000000 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/DeploymentEvent.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright (c) 2015 Bosch Software Innovations 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.repository.event.remote; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -/** - * TenantAwareEvent that gets sent when a distribution set gets assigned to a - * target. - */ -public abstract class DeploymentEvent extends RemoteTenantAwareEvent implements Iterable { - - private static final long serialVersionUID = 1L; - - private final List controllerIds = new ArrayList<>(); - - /** - * Default constructor. - */ - public DeploymentEvent() { - // for serialization libs like jackson - } - - /** - * Constructor. - * - * @param tenant - * of the event - * @param applicationId - * the application id. - * @param controllerIds - * the controller IDs - */ - public DeploymentEvent(final String tenant, final String applicationId, final List controllerIds) { - super(applicationId, tenant, applicationId); - this.controllerIds.addAll(controllerIds); - } - - public List getControllerIds() { - return controllerIds; - } - - @Override - public Iterator iterator() { - return controllerIds.iterator(); - } - -} diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionEvent.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionEvent.java index 3a61f57246..e049d03574 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionEvent.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/event/remote/MultiActionEvent.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * Copyright (c) 2019 Bosch Software Innovations 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 @@ -8,36 +8,51 @@ */ package org.eclipse.hawkbit.repository.event.remote; +import java.util.ArrayList; +import java.util.Iterator; import java.util.List; /** - * Deployment event which implies that the Multi-Assignment feature is enabled. + * Generic deployment event for the Multi-Assignments feature. The event payload + * holds a list of controller IDs identifying the targets which are affected by + * a deployment action (e.g. a software assignment (update) or a cancellation of + * an update). */ -@SuppressWarnings("squid:MaximumInheritanceDepth") -public class MultiActionEvent extends DeploymentEvent { +public class MultiActionEvent extends RemoteTenantAwareEvent implements Iterable { private static final long serialVersionUID = 1L; + private final List controllerIds = new ArrayList<>(); + /** - * Default constructor for serialization purposes. + * Default constructor. */ public MultiActionEvent() { - super(); + // for serialization libs like jackson } /** - * Constructs a Multi-Action event for the targets with the given controller - * IDs. + * Constructor. * * @param tenant - * this event is scoped to + * tenant the event is scoped to * @param applicationId - * of the application + * the application id * @param controllerIds - * of the targets this event refers to + * the controller IDs of the affected targets */ public MultiActionEvent(final String tenant, final String applicationId, final List controllerIds) { - super(tenant, applicationId, controllerIds); + super(applicationId, tenant, applicationId); + this.controllerIds.addAll(controllerIds); + } + + public List getControllerIds() { + return controllerIds; + } + + @Override + public Iterator iterator() { + return controllerIds.iterator(); } -} +} \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java index 9b8aca879b..452abfb090 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaDeploymentManagement.java @@ -433,11 +433,7 @@ public Action cancelAction(final long actionId) { RepositoryConstants.SERVER_MESSAGE_PREFIX + "manual cancelation requested")); final Action saveAction = actionRepository.save(action); - if (isMultiAssignmentsEnabled()) { - onlineDsAssignmentStrategy.sendDeploymentEvent(action.getTarget()); - } else { - onlineDsAssignmentStrategy.cancelAssignDistributionSetEvent(action.getTarget(), action.getId()); - } + onlineDsAssignmentStrategy.cancelAssignment(action); return saveAction; } else { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java index 2b5c536ac5..cfc1a7a981 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java @@ -66,10 +66,6 @@ void sendTargetUpdatedEvents(final DistributionSet set, final List ta }); } - void sendDeploymentEvent(final Target target) { - sendMultiActionEvent(target.getTenant(), Collections.singletonList(target.getControllerId())); - } - @Override void sendDeploymentEvents(final DistributionSetAssignmentResult assignmentResult) { if (isMultiAssignmentsEnabled()) { @@ -105,19 +101,6 @@ void sendDeploymentEvents(final long distributionSetId, final List actio } } - private void sendDeploymentEvent(final List actions) { - final List filteredActions = actions.stream().filter(action -> { - final Status actionStatus = action.getStatus(); - return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; - }).collect(Collectors.toList()); - if (filteredActions.isEmpty()) { - return; - } - final String tenant = filteredActions.get(0).getTenant(); - sendMultiActionEvent(tenant, filteredActions.stream().map(action -> action.getTarget().getControllerId()) - .collect(Collectors.toList())); - } - @Override List findTargetsForAssignment(final List controllerIDs, final long setId) { final Function, List> mapper; @@ -167,6 +150,31 @@ JpaActionStatus createActionStatus(final JpaAction action, final String actionMe return result; } + void cancelAssignment(final JpaAction action) { + if (isMultiAssignmentsEnabled()) { + sendMultiActionEvent(action.getTarget()); + } else { + cancelAssignDistributionSetEvent(action.getTarget(), action.getId()); + } + } + + private void sendMultiActionEvent(final Target target) { + sendMultiActionEvent(target.getTenant(), Collections.singletonList(target.getControllerId())); + } + + private void sendDeploymentEvent(final List actions) { + final List filteredActions = actions.stream().filter(action -> { + final Status actionStatus = action.getStatus(); + return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; + }).collect(Collectors.toList()); + if (filteredActions.isEmpty()) { + return; + } + final String tenant = filteredActions.get(0).getTenant(); + sendMultiActionEvent(tenant, filteredActions.stream().map(action -> action.getTarget().getControllerId()) + .collect(Collectors.toList())); + } + private DistributionSetAssignmentResult sendDistributionSetAssignedEvent( final DistributionSetAssignmentResult assignmentResult) { final List filteredActions = assignmentResult.getActions().stream().filter(action -> { From 353e9313e66c88e0218551fbfc784fa6b752b5d2 Mon Sep 17 00:00:00 2001 From: Stefan Behl Date: Wed, 15 May 2019 11:21:57 +0200 Subject: [PATCH 56/56] Fix PR review findings Signed-off-by: Stefan Behl --- .../AbstractAmqpServiceIntegrationTest.java | 23 ------- ...ssageDispatcherServiceIntegrationTest.java | 17 +++++ .../jpa/JpaControllerManagement.java | 2 +- .../jpa/OnlineDsAssignmentStrategy.java | 54 +++++++++------ .../jpa/DeploymentManagementTest.java | 35 +++++----- .../rest/resource/DdiRootControllerTest.java | 67 +++++++++---------- .../MgmtTenantManagementResourceTest.java | 18 ++--- .../management/targettable/TargetTable.java | 8 +-- .../MultiAssignmentsConfigurationItem.java | 6 +- .../src/main/resources/messages.properties | 1 - 10 files changed, 117 insertions(+), 114 deletions(-) diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java index ffccf333cd..57fc5ac99d 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AbstractAmqpServiceIntegrationTest.java @@ -11,16 +11,12 @@ import static org.assertj.core.api.Assertions.assertThat; import java.nio.charset.StandardCharsets; -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.amqp.DmfApiConfiguration; import org.eclipse.hawkbit.dmf.amqp.api.AmqpSettings; @@ -33,8 +29,6 @@ import org.eclipse.hawkbit.dmf.json.model.DmfAttributeUpdate; 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.DmfMultiActionRequest.DmfMultiActionElement; import org.eclipse.hawkbit.integration.listener.DeadletterListener; import org.eclipse.hawkbit.integration.listener.ReplyToListener; import org.eclipse.hawkbit.matcher.SoftwareModuleJsonMatcher; @@ -212,23 +206,6 @@ protected void assertDownloadAndInstallMessage(final Set softwar } - protected void assertLatestMultiActionMessage(final String controllerId, - final List> actionsExpected) { - final List> actionsFromMessage = getLatestMultiActionMessageActions(controllerId); - assertThat(actionsFromMessage).containsExactlyInAnyOrderElementsOf(actionsExpected); - } - - protected List> getLatestMultiActionMessageActions(final String expectedControllerId) { - final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION); - assertThat(multiactionMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID)) - .isEqualTo(expectedControllerId); - final List multiActionRequest = ((DmfMultiActionRequest) getDmfClient() - .getMessageConverter().fromMessage(multiactionMessage)).getElements(); - return multiActionRequest.stream() - .map(request -> new SimpleEntry<>(request.getAction().getActionId(), request.getTopic())) - .collect(Collectors.toList()); - } - protected void assertDownloadMessage(final Set dsModules, final String controllerId) { assertAssignmentMessage(dsModules, controllerId, EventTopic.DOWNLOAD); } diff --git a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java index b15c9e0400..c2c626c647 100644 --- a/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java +++ b/hawkbit-dmf/hawkbit-dmf-amqp/src/test/java/org/eclipse/hawkbit/integration/AmqpMessageDispatcherServiceIntegrationTest.java @@ -482,6 +482,23 @@ private void assertLatestMultiActionMessageContainsInstallMessages(final String assertThat(smIdsOfActionsFound).containsExactlyInAnyOrderElementsOf(smIdsOfActionsExpected); } + private void assertLatestMultiActionMessage(final String controllerId, + final List> actionsExpected) { + final List> actionsFromMessage = getLatestMultiActionMessageActions(controllerId); + assertThat(actionsFromMessage).containsExactlyInAnyOrderElementsOf(actionsExpected); + } + + private List> getLatestMultiActionMessageActions(final String expectedControllerId) { + final Message multiactionMessage = replyToListener.getLatestEventMessage(EventTopic.MULTI_ACTION); + assertThat(multiactionMessage.getMessageProperties().getHeaders().get(MessageHeaderKey.THING_ID)) + .isEqualTo(expectedControllerId); + final List 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 getSmIds(final DmfDownloadAndUpdateRequest request) { return request.getSoftwareModules().stream().map(DmfSoftwareModule::getModuleId).collect(Collectors.toSet()); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java index cbfbad5d44..bc56c0d2ba 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaControllerManagement.java @@ -649,7 +649,7 @@ private String handleDownloadedActionStatus(final JpaAction action) { return null; } - JpaTarget target = (JpaTarget) action.getTarget(); + final JpaTarget target = (JpaTarget) action.getTarget(); action.setActive(false); action.setStatus(DOWNLOADED); target.setUpdateStatus(TargetUpdateStatus.IN_SYNC); diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java index cfc1a7a981..c1d8c886fb 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/OnlineDsAssignmentStrategy.java @@ -8,6 +8,8 @@ */ package org.eclipse.hawkbit.repository.jpa; +import static org.eclipse.hawkbit.repository.RepositoryConstants.MAX_ACTION_COUNT; + import java.util.Collection; import java.util.Collections; import java.util.List; @@ -16,6 +18,7 @@ import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.eclipse.hawkbit.repository.QuotaManagement; import org.eclipse.hawkbit.repository.event.remote.MultiActionEvent; @@ -36,6 +39,8 @@ import org.eclipse.hawkbit.repository.model.TargetWithActionType; import org.springframework.cloud.bus.BusProperties; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.util.CollectionUtils; import com.google.common.collect.Lists; @@ -46,6 +51,8 @@ */ public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy { + private static final Pageable ACTION_PAGE_REQUEST = PageRequest.of(0, MAX_ACTION_COUNT); + private final Supplier multiAssignmentsConfig; OnlineDsAssignmentStrategy(final TargetRepository targetRepository, @@ -88,17 +95,14 @@ void sendDeploymentEvents(final List assignment void sendDeploymentEvents(final long distributionSetId, final List actions) { if (isMultiAssignmentsEnabled()) { sendDeploymentEvent(actions); - } else { - final List filteredActions = actions.stream().filter(action -> { - final Status actionStatus = action.getStatus(); - return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; - }).collect(Collectors.toList()); - if (filteredActions.isEmpty()) { - return; - } - sendTargetAssignDistributionSetEvent(filteredActions.get(0).getTenant(), distributionSetId, - filteredActions); + return; + } + + final List filteredActions = getActionsWithoutCancellations(actions); + if (filteredActions.isEmpty()) { + return; } + sendTargetAssignDistributionSetEvent(filteredActions.get(0).getTenant(), distributionSetId, filteredActions); } @Override @@ -163,10 +167,7 @@ private void sendMultiActionEvent(final Target target) { } private void sendDeploymentEvent(final List actions) { - final List filteredActions = actions.stream().filter(action -> { - final Status actionStatus = action.getStatus(); - return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; - }).collect(Collectors.toList()); + final List filteredActions = getActionsWithoutCancellations(actions); if (filteredActions.isEmpty()) { return; } @@ -177,11 +178,8 @@ private void sendDeploymentEvent(final List actions) { private DistributionSetAssignmentResult sendDistributionSetAssignedEvent( final DistributionSetAssignmentResult assignmentResult) { - final List filteredActions = assignmentResult.getActions().stream().filter(action -> { - final Status actionStatus = action.getStatus(); - return !hasPendingCancellations(action.getTarget()) && Status.CANCELING != actionStatus - && Status.CANCELED != actionStatus; - }).collect(Collectors.toList()); + final List filteredActions = filterCancellations(assignmentResult.getActions()) + .filter(action -> !hasPendingCancellations(action.getTarget())).collect(Collectors.toList()); final DistributionSet set = assignmentResult.getDistributionSet(); sendTargetAssignDistributionSetEvent(set.getTenant(), set.getId(), filteredActions); return assignmentResult; @@ -198,8 +196,8 @@ private void sendTargetAssignDistributionSetEvent(final String tenant, final lon } private boolean hasPendingCancellations(final Target target) { - return actionRepository.findByActiveAndTarget(null, target.getControllerId(), true).getContent().stream() - .anyMatch(action -> action.getStatus() == Status.CANCELING); + return actionRepository.findByActiveAndTarget(ACTION_PAGE_REQUEST, target.getControllerId(), true).getContent() + .stream().anyMatch(action -> action.getStatus() == Status.CANCELING); } /** @@ -220,4 +218,18 @@ private boolean isMultiAssignmentsEnabled() { return multiAssignmentsConfig.get(); } + private static Stream filterCancellations(final List actions) { + return actions.stream().filter(action -> { + final Status actionStatus = action.getStatus(); + return Status.CANCELING != actionStatus && Status.CANCELED != actionStatus; + }); + } + + private static List getActionsWithoutCancellations(final List actions) { + if (actions == null || actions.isEmpty()) { + return Collections.emptyList(); + } + return filterCancellations(actions).collect(Collectors.toList()); + } + } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java index f07ad0d8eb..eb64b1ed11 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/DeploymentManagementTest.java @@ -86,6 +86,10 @@ @Feature("Component Tests - Repository") @Story("Deployment Management") public class DeploymentManagementTest extends AbstractJpaIntegrationTest { + + private static final boolean STATE_ACTIVE = true; + private static final boolean STATE_INACTIVE = false; + private EventHandlerStub eventHandlerStub; private CancelEventHandlerStub cancelEventHandlerStub; @@ -486,8 +490,8 @@ public void assignedDistributionSet() { final List targets = deploymentManagement.offlineAssignedDistributionSet(ds.getId(), controllerIds) .getAssignedEntity(); assertThat(actionRepository.count()).isEqualTo(20); - assertThat(actionRepository.findByDistributionSetId(PAGE, ds.getId())) - .as("Offline actions are not active").allMatch(action -> !action.isActive()); + assertThat(actionRepository.findByDistributionSetId(PAGE, ds.getId())).as("Offline actions are not active") + .allMatch(action -> !action.isActive()); assertThat(targetManagement.findByInstalledDistributionSet(PAGE, ds.getId()).getContent()).containsAll(targets) .hasSize(10).containsAll(targetManagement.findByAssignedDistributionSet(PAGE, ds.getId())) .as("InstallationDate set").allMatch(target -> target.getInstallationDate() >= current) @@ -516,14 +520,14 @@ public void assignDistributionSetAndAutoCloseActiveActions() { final DistributionSet ds1 = testdataFactory.createDistributionSet("1"); assignDistributionSet(ds1, targets); - assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), true, Status.RUNNING); + assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_ACTIVE, Status.RUNNING); // Second assignment final DistributionSet ds2 = testdataFactory.createDistributionSet("2"); assignDistributionSet(ds2, targets); - assertDsExclusivelyAssignedToTargets(targets, ds2.getId(), true, Status.RUNNING); - assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), false, Status.CANCELED); + assertDsExclusivelyAssignedToTargets(targets, ds2.getId(), STATE_ACTIVE, Status.RUNNING); + assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_INACTIVE, Status.CANCELED); assertThat(targetManagement.findByAssignedDistributionSet(PAGE, ds2.getId()).getContent()).hasSize(10) .as("InstallationDate not set").allMatch(target -> (target.getInstallationDate() == null)); @@ -535,7 +539,7 @@ public void assignDistributionSetAndAutoCloseActiveActions() { } @Test - @Description("Verifies that if multi assignment is enabled previous Distribution is not canceled when a new one is assigned.") + @Description("If multi-assignment is enabled, verify that the previous Distribution Set assignment is not canceled when a new one is assigned.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 10), @Expect(type = TargetUpdatedEvent.class, count = 20), @Expect(type = ActionCreatedEvent.class, count = 20), @Expect(type = DistributionSetCreatedEvent.class, count = 2), @@ -543,28 +547,28 @@ public void assignDistributionSetAndAutoCloseActiveActions() { @Expect(type = MultiActionEvent.class, count = 2), @Expect(type = TargetAssignDistributionSetEvent.class, count = 0) }) public void previousAssignmentsAreNotCanceledInMultiAssignMode() { - setMultiAssignmentsEnabled(true); + enableMultiAssignments(); final List targets = testdataFactory.createTargets(10); // First assignment final DistributionSet ds1 = testdataFactory.createDistributionSet("Multi-assign-1"); assignDistributionSet(ds1, targets); - assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), true, Status.RUNNING); + assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_ACTIVE, Status.RUNNING); // Second assignment final DistributionSet ds2 = testdataFactory.createDistributionSet("Multi-assign-2"); assignDistributionSet(ds2, targets); - assertDsExclusivelyAssignedToTargets(targets, ds2.getId(), true, Status.RUNNING); - assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), true, Status.RUNNING); + assertDsExclusivelyAssignedToTargets(targets, ds2.getId(), STATE_ACTIVE, Status.RUNNING); + assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_ACTIVE, Status.RUNNING); } private void assertDsExclusivelyAssignedToTargets(final List targets, final long dsId, final boolean active, final Status status) { final List assignment = actionRepository.findByDistributionSetId(PAGE, dsId).getContent(); - assertThat(assignment).hasSize(10).as("Active = " + active).allMatch(action -> action.isActive() == active) + assertThat(assignment).hasSize(10).allMatch(action -> action.isActive() == active) .as("Is assigned to DS " + dsId).allMatch(action -> action.getDistributionSet().getId().equals(dsId)) .as("State is " + status).allMatch(action -> action.getStatus() == status); final long[] targetIds = targets.stream().mapToLong(Target::getId).toArray(); @@ -1119,9 +1123,7 @@ private void assertTargetAssignDistributionSetEvents(final List targets, final List targetActiveActionIds = targets.stream() .map(t -> deploymentManagement.findActiveActionsByTarget(PAGE, t.getControllerId()).getContent()) - .flatMap(List::stream) - .map(Action::getId) - .collect(Collectors.toList()); + .flatMap(List::stream).map(Action::getId).collect(Collectors.toList()); assertThat(eventActionIds).containsOnlyElementsOf(targetActiveActionIds); } @@ -1243,9 +1245,8 @@ public void onApplicationEvent(final CancelTargetAssignmentEvent event) { } } - private void setMultiAssignmentsEnabled(final boolean enable) { - tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, - enable); + private void enableMultiAssignments() { + tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED, true); } } diff --git a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java index 8f1a595c1a..90a307f627 100644 --- a/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java +++ b/hawkbit-rest/hawkbit-ddi-resource/src/test/java/org/eclipse/hawkbit/ddi/rest/resource/DdiRootControllerTest.java @@ -260,7 +260,7 @@ public void rootRsNotModified() throws Exception { etagWithFirstUpdate)).andDo(MockMvcResultPrinter.print()).andExpect(status().isNotModified()); // now lets finish the update - sendDeploymentActionFeedback(target, updateAction, "closed", null, null).andDo(MockMvcResultPrinter.print()) + sendDeploymentActionFeedback(target, updateAction, "closed", null).andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()); // we are again at the original state @@ -373,14 +373,14 @@ public void tryToFinishAnUpdateProcessAfterItHasBeenFinished() throws Exception .next(); final Action savedAction = deploymentManagement.findActiveActionsByTarget(PAGE, savedTarget.getControllerId()) .getContent().get(0); - sendDeploymentActionFeedback(savedTarget, savedAction, "proceeding", null, null) - .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); + sendDeploymentActionFeedback(savedTarget, savedAction, "proceeding", null).andDo(MockMvcResultPrinter.print()) + .andExpect(status().isOk()); - sendDeploymentActionFeedback(savedTarget, savedAction, "closed", "failure", null) - .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); + sendDeploymentActionFeedback(savedTarget, savedAction, "closed", "failure").andDo(MockMvcResultPrinter.print()) + .andExpect(status().isOk()); - sendDeploymentActionFeedback(savedTarget, savedAction, "closed", "success", null) - .andDo(MockMvcResultPrinter.print()).andExpect(status().isGone()); + sendDeploymentActionFeedback(savedTarget, savedAction, "closed", "success").andDo(MockMvcResultPrinter.print()) + .andExpect(status().isGone()); } @Test @@ -416,7 +416,7 @@ private void assertAttributesUpdateNotRequestedAfterFailedDeployment(Target targ target = assignDistributionSet(ds.getId(), target.getControllerId()).getAssignedEntity().iterator().next(); final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId()) .getContent().get(0); - sendDeploymentActionFeedback(target, action, "closed", "failure", null).andExpect(status().isOk()); + sendDeploymentActionFeedback(target, action, "closed", "failure").andExpect(status().isOk()); assertThatAttributesUpdateIsNotRequested(target.getControllerId()); } @@ -426,7 +426,7 @@ private void assertAttributesUpdateRequestedAfterSuccessfulDeployment(Target tar target = assignDistributionSet(ds.getId(), target.getControllerId()).getAssignedEntity().iterator().next(); final Action action = deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId()) .getContent().get(0); - sendDeploymentActionFeedback(target, action, "closed", null, null).andExpect(status().isOk()); + sendDeploymentActionFeedback(target, action, "closed", null).andExpect(status().isOk()); assertThatAttributesUpdateIsRequested(target.getControllerId()); } @@ -444,10 +444,12 @@ private void assertThatAttributesUpdateIsNotRequested(final String targetControl private ResultActions sendDeploymentActionFeedback(final Target target, final Action action, final String execution, String finished, String message) throws Exception { - if (finished == null) + if (finished == null) { finished = "none"; - if (message == null) + } + if (message == null) { message = RandomStringUtils.randomAlphanumeric(1000); + } final String feedback = JsonBuilder.deploymentActionFeedback(action.getId().toString(), execution, finished, message); return mvc.perform(post("/{tenant}/controller/v1/{controllerId}/deploymentBase/{actionId}/feedback", @@ -455,6 +457,11 @@ private ResultActions sendDeploymentActionFeedback(final Target target, final Ac .contentType(MediaType.APPLICATION_JSON)); } + private ResultActions sendDeploymentActionFeedback(final Target target, final Action action, final String execution, + final String finished) throws Exception { + return sendDeploymentActionFeedback(target, action, execution, finished, null); + } + @Test @Description("Test to verify that only a specific count of messages are returned based on the input actionHistory for getControllerDeploymentActionFeedback endpoint.") @ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1), @@ -473,16 +480,13 @@ public void testActionHistoryCount() throws Exception { .getContent().get(0); sendDeploymentActionFeedback(savedTarget, savedAction, "scheduled", null, TARGET_SCHEDULED_INSTALLATION_MSG) - .andDo(MockMvcResultPrinter.print()) - .andExpect(status().isOk()); + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); sendDeploymentActionFeedback(savedTarget, savedAction, "proceeding", null, TARGET_PROCEEDING_INSTALLATION_MSG) - .andDo(MockMvcResultPrinter.print()) - .andExpect(status().isOk()); + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); sendDeploymentActionFeedback(savedTarget, savedAction, "closed", "success", TARGET_COMPLETED_INSTALLATION_MSG) - .andDo(MockMvcResultPrinter.print()) - .andExpect(status().isOk()); + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); mvc.perform(get("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "?actionHistory=3", tenantAware.getCurrentTenant()).contentType(MediaType.APPLICATION_JSON) @@ -512,16 +516,13 @@ public void testActionHistoryZeroInput() throws Exception { .getContent().get(0); sendDeploymentActionFeedback(savedTarget, savedAction, "scheduled", null, TARGET_SCHEDULED_INSTALLATION_MSG) - .andDo(MockMvcResultPrinter.print()) - .andExpect(status().isOk()); + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); sendDeploymentActionFeedback(savedTarget, savedAction, "proceeding", null, TARGET_PROCEEDING_INSTALLATION_MSG) - .andDo(MockMvcResultPrinter.print()) - .andExpect(status().isOk()); + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); sendDeploymentActionFeedback(savedTarget, savedAction, "closed", "success", TARGET_COMPLETED_INSTALLATION_MSG) - .andDo(MockMvcResultPrinter.print()) - .andExpect(status().isOk()); + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); mvc.perform(get("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "?actionHistory=-2", tenantAware.getCurrentTenant()).contentType(MediaType.APPLICATION_JSON) @@ -547,16 +548,13 @@ public void testActionHistoryNegativeInput() throws Exception { .getContent().get(0); sendDeploymentActionFeedback(savedTarget, savedAction, "scheduled", null, TARGET_SCHEDULED_INSTALLATION_MSG) - .andDo(MockMvcResultPrinter.print()) - .andExpect(status().isOk()); + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); sendDeploymentActionFeedback(savedTarget, savedAction, "proceeding", null, TARGET_PROCEEDING_INSTALLATION_MSG) - .andDo(MockMvcResultPrinter.print()) - .andExpect(status().isOk()); + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); sendDeploymentActionFeedback(savedTarget, savedAction, "closed", "success", TARGET_COMPLETED_INSTALLATION_MSG) - .andDo(MockMvcResultPrinter.print()) - .andExpect(status().isOk()); + .andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()); mvc.perform(get("/{tenant}/controller/v1/911/deploymentBase/" + savedAction.getId() + "?actionHistory=-1", tenantAware.getCurrentTenant()).contentType(MediaType.APPLICATION_JSON) @@ -658,8 +656,8 @@ public void downloadAndUpdateStatusDuringMaintenanceWindow() throws Exception { } @Test - @Description("Assign multiple DS in multiassignment mode. The earliest active Action is exposed to the target.") - public void earliestActionIsExposedToTargetInMultiAssignMode() throws Exception { + @Description("Assign multiple DS in multi-assignment mode. The earliest active Action is exposed to the controller.") + public void earliestActionIsExposedToControllerInMultiAssignMode() throws Exception { setMultiAssignmentsEnabled(); final Target target = testdataFactory.createTarget(); final DistributionSet ds1 = testdataFactory.createDistributionSet(UUID.randomUUID().toString()); @@ -668,7 +666,7 @@ public void earliestActionIsExposedToTargetInMultiAssignMode() throws Exception final Action action2 = assignDistributionSet(ds2, target).getActions().get(0); assertDeploymentActionIsExposedToTarget(target.getControllerId(), action1.getId()); - sendDeploymentActionFeedback(target, action1, "closed", "success", null); + sendDeploymentActionFeedback(target, action1, "closed", "success"); assertDeploymentActionIsExposedToTarget(target.getControllerId(), action2.getId()); } @@ -677,9 +675,8 @@ public void assertDeploymentActionIsExposedToTarget(final String controllerId, f throws Exception { final String expectedDeploymentBaseLink = String.format("/%s/controller/v1/%s/deploymentBase/%d", tenantAware.getCurrentTenant(), controllerId, expectedActionId); - mvc.perform(get("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(), - controllerId).accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) - .andExpect(status().isOk()) + mvc.perform(get("/{tenant}/controller/v1/{controllerId}", tenantAware.getCurrentTenant(), controllerId) + .accept(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()) .andExpect(jsonPath("$._links.deploymentBase.href", containsString(expectedDeploymentBaseLink))); } diff --git a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java index b369b827aa..ac16e74e76 100644 --- a/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java +++ b/hawkbit-rest/hawkbit-mgmt-resource/src/test/java/org/eclipse/hawkbit/mgmt/rest/resource/MgmtTenantManagementResourceTest.java @@ -29,18 +29,21 @@ @Story("Tenant Management Resource") public class MgmtTenantManagementResourceTest extends AbstractManagementApiIntegrationTest { + private static final String KEY_MULTI_ASSIGNMENTS = "multi.assignments.enabled"; + + private static final String KEY_AUTO_CLOSE = "repository.actions.autoclose.enabled"; + @Test @Description("The 'multi.assignments.enabled' property must not be changed to false.") public void deactivateMultiAssignment() throws Exception { - final String multiAssignmentKey = "multi.assignments.enabled"; final String bodyActivate = new JSONObject().put("value", true).toString(); final String bodyDeactivate = new JSONObject().put("value", false).toString(); - mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", multiAssignmentKey) + mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", KEY_MULTI_ASSIGNMENTS) .content(bodyActivate).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()); - mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", multiAssignmentKey) + mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", KEY_MULTI_ASSIGNMENTS) .content(bodyDeactivate).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isForbidden()); } @@ -48,24 +51,21 @@ public void deactivateMultiAssignment() throws Exception { @Test @Description("The 'repository.actions.autoclose.enabled' property must not be modified if Multi-Assignments is enabled.") public void autoCloseCannotBeModifiedIfMultiAssignmentIsEnabled() throws Exception { - final String multiAssignmentKey = "multi.assignments.enabled"; - final String autoCloseKey = "repository.actions.autoclose.enabled"; - final String bodyActivate = new JSONObject().put("value", true).toString(); final String bodyDeactivate = new JSONObject().put("value", false).toString(); // enable Multi-Assignments - mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", multiAssignmentKey) + mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", KEY_MULTI_ASSIGNMENTS) .content(bodyActivate).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isOk()); // try to enable Auto-Close - mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", autoCloseKey) + mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", KEY_AUTO_CLOSE) .content(bodyActivate).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isForbidden()); // try to disable Auto-Close - mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", autoCloseKey) + mvc.perform(put(MgmtRestConstants.SYSTEM_V1_REQUEST_MAPPING + "/configs/{keyName}", KEY_AUTO_CLOSE) .content(bodyDeactivate).contentType(MediaType.APPLICATION_JSON)).andDo(MockMvcResultPrinter.print()) .andExpect(status().isForbidden()); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java index 0e9997d363..c2ea3e1404 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/management/targettable/TargetTable.java @@ -889,13 +889,13 @@ private void assignDsToTarget(final DragAndDropEvent event) { } private Set filterDistributionSetsToAssign(final Set ids) { + if (ids.isEmpty()) { + return Collections.emptySet(); + } if (isMultiAssignmentsEnabled()) { return new HashSet<>(ids); } - if (!ids.isEmpty()) { - return Collections.singleton(ids.iterator().next()); - } - return Collections.emptySet(); + return Collections.singleton(ids.iterator().next()); } private void openConfirmationWindowForAssignments(final Target target, diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/repository/MultiAssignmentsConfigurationItem.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/repository/MultiAssignmentsConfigurationItem.java index b65378ad6c..83dc32b765 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/repository/MultiAssignmentsConfigurationItem.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/tenantconfiguration/repository/MultiAssignmentsConfigurationItem.java @@ -1,5 +1,5 @@ /** - * Copyright (c) 2015 Bosch Software Innovations GmbH and others. + * Copyright (c) 2019 Bosch Software Innovations 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 @@ -18,8 +18,8 @@ import com.vaadin.ui.VerticalLayout; /** - * This class represents the UI item for the target security token section in - * the authentication configuration view. + * This class represents the UI item for enabling /disabling the + * Multi-Assignments feature as part of the repository configuration view. */ public class MultiAssignmentsConfigurationItem extends AbstractBooleanTenantConfigurationItem { diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index 7d976e005e..590bf4af94 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -709,7 +709,6 @@ message.confirm.delete.entity = Are you sure you want to delete {0} {1}{2}? caption.entity.assign.action.confirmbox = Confirm Assignment message.confirm.assign.entity = Are you sure you want to assign distribution {0} to {1} {2}? message.confirm.assign.multiple.entities = Are you sure you want to assign {0} {1} to distribution {2}? -#message.confirm.assign.multiple.entities.multiple.distributions = Are you sure you want to assign {0} {1} to the distributions {2}? message.confirm.assign.multiple.entities.multiple.distributions = Are you sure you want to assign {0} distributions to {1} {2}? # character descriptions