Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature mass cancel running actions on ds invalidation #1177

2 changes: 2 additions & 0 deletions docs/content/concepts/rollout-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Software update operations in large scale IoT scenarios with hundreds of thousan
<!--more-->

That includes:

- _Technical Scalability_ by means of horizontal scale of the hawkBit server cluster in the cloud.
- _Global_ artifact _content delivery_ capacities.
- _Functional Scalability_ by means of:
Expand All @@ -20,6 +21,7 @@ That includes:
Eclipse hawkBit sees these capabilities under the term Rollout Management.

The following capabilities are currently supported by the _Rollout Management_:

- Create, update and start of rollouts.
- Selection of targets as input for the rollout based on _target filter_ functionality.
- Selection of a _DistributionSet_.
Expand Down
20 changes: 17 additions & 3 deletions docs/content/ui.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ Target status overview, target management and manual deployments.

- Start roll out by drag and drop targets on a DS.
- Target list supports CTRL-A for "select all".
- Delete sets, tags or targets by dragging them on delete icon.
- Select _Target_ to see _Action_ History.
- Bulk target upload: create bulk targets by upload.

- DS invalidation allows to mark broken updates and avoid the distribution of such

Hints for bulk upload:

- Expected file type : csv.
- Expected file format : Each line with two values (ControllerID,Target Name). ControllerID is mandatory.
- Example:
Expand All @@ -47,6 +47,20 @@ Controller_id_2,targetName2

![Deployment Management view](../images/ui/deployment_mgmt.png)

### Distribution set invalidation

It is possible to mark broken updates and avoid the distribution of such by invalidating the corresponding distribution
set in the Distributions list of the Deployment view.

Invalidating a distribution set removes all auto-assignments that reference this distribution set. Optionally, all
rollouts that reference the distribution set can be stopped and existing update actions are removed, either by a
soft-cancel or a forced-cancel.

Invalidated distribution sets cannot be valid again, but remain invalid. They cannot be assigned to targets, neither
through a rollout, auto-assignment nor a single assignment.

![Distribution set invalidation](../images/ui/deployment_ds_invalidation.png)

## Distribution Management

### Purpose
Expand Down Expand Up @@ -148,4 +162,4 @@ In order to activate the auto-assignment, one should first click on _Auto assign

As long as the auto-assignment stays active, the scheduler will try to assign selected distribution set to corresponding custom filter targets, that have never seen it before.

![Auto assignment](../images/ui/target_filter_auto_assignment.png)
![Auto assignment](../images/ui/target_filter_auto_assignment.png)
Binary file modified docs/static/images/ui/artifact_mgmt.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/static/images/ui/deployment_mgmt.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/static/images/ui/distribution_mgmt.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/static/images/ui/rollout_groups.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/static/images/ui/rollout_mgmt.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/static/images/ui/target_filter.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/static/images/ui/target_filter_auto_assignment.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public void setCancelRollouts(final boolean cancelRollouts) {
* distribution set
*/
public enum CancelationType {
FORCE, SOFT, NONE;
FORCE, SOFT, NONE
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ public long getActionCount() {
return actionCount;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,12 @@ include::../errors/429.adoc[]

=== Implementation Notes

Invalidate a distribution set. Once a distribution set is invalidated, it can not be valid again. An invalidated distribution set cannot be assigned to targets anymore. The distribution set that is going to be invalidated will be removed from all auto assignments. Furthermore, the user can choose to cancel all rollouts and (force) cancel all actions connected to this distribution set. Required permission: UPDATE_REPOSITORY
Invalidate a distribution set.
Once a distribution set is invalidated, it can not be valid again.
An invalidated distribution set cannot be assigned to targets anymore.
The distribution set that is going to be invalidated will be removed from all auto assignments.
Furthermore, the user can choose to cancel all rollouts and (force) cancel all actions connected to this distribution set.
Required permission: UPDATE_REPOSITORY, UPDATE_TARGET

=== Invalidate a distribution set

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,22 @@ public boolean hasRolloutTargetsReadPermission() {
public boolean hasRolloutApprovalPermission() {
return hasRolloutReadPermission() && permissionService.hasPermission(SpPermission.APPROVE_ROLLOUT);
}

/**
*
* @return <code>true</code> if auto assignment can be added/updated to target filter
*/
public boolean hasAutoAssignmentUpdatePermission() {
return hasUpdateTargetPermission() && hasReadRepositoryPermission();
}

/**
*
* @return <code>true</code> if default invalidation of distribution set is
* allowed
*/
public boolean hasDistributionSetInvalidatePermission() {
return hasUpdateRepositoryPermission() && hasUpdateTargetPermission();
}

}
24 changes: 24 additions & 0 deletions hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/UiProperties.java
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,16 @@ public static class Documentation implements Serializable {
*/
private String rolloutView = "";

/**
* Link to documentation of state machine
*/
private String provisioningStateMachine = "";

/**
* Link to documentation of distribution set invalidation
*/
private String distributionSetInvalidation = "";

/**
* @return Link to documentation of deployment view
*/
Expand Down Expand Up @@ -352,6 +360,13 @@ public String getProvisioningStateMachine() {
return provisioningStateMachine;
}

/**
* @return Link to documentation of distribution set invalidation
*/
public String getDistributionSetInvalidation() {
return distributionSetInvalidation;
}

public void setDeploymentView(final String deploymentView) {
this.deploymentView = deploymentView;
}
Expand Down Expand Up @@ -414,6 +429,15 @@ public void setProvisioningStateMachine(final String provisioningStateMachine) {
this.provisioningStateMachine = provisioningStateMachine;
}

/**
* Sets the link to the distribution set invalidation documentation
*
* @param distributionSetInvalidation
* Link
*/
public void setDistributionSetInvalidation(String distributionSetInvalidation) {
this.distributionSetInvalidation = distributionSetInvalidation;
}
}

private final Documentation documentation = new Documentation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,8 @@ private boolean isDropValid(final String sourceId, final T dropTargetItem,

final List<String> requiredPermissions = assignmentStrategy.getMissingPermissionsForDrop();
if (!CollectionUtils.isEmpty(requiredPermissions)) {
notification
.displayValidationError(i18n.getMessage("message.permission.insufficient", requiredPermissions));
notification.displayValidationError(
i18n.getMessage(UIMessageIdProvider.MESSAGE_ERROR_PERMISSION_INSUFFICIENT, requiredPermissions));
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ private Button buildAutoAssignmentLink(final ProxyTargetFilterQuery targetFilter
}

final Button link = GridComponentBuilder.buildLink(targetFilter, "distSetButton", caption,
permissionChecker.hasReadRepositoryPermission(),
permissionChecker.hasAutoAssignmentUpdatePermission(),
clickEvent -> onClickOfAutoAssignmentLink(targetFilter));

final String description = i18n.getMessage(UIMessageIdProvider.BUTTON_AUTO_ASSIGNMENT_DESCRIPTION);
Expand All @@ -202,8 +202,8 @@ private void onClickOfAutoAssignmentLink(final ProxyTargetFilterQuery targetFilt
UI.getCurrent().addWindow(autoAssignmentWindow);
autoAssignmentWindow.setVisible(Boolean.TRUE);
} else {
notification.displayValidationError(
i18n.getMessage("message.permission.insufficient", SpPermission.READ_REPOSITORY));
notification.displayValidationError(i18n.getMessage(
UIMessageIdProvider.MESSAGE_ERROR_PERMISSION_INSUFFICIENT, SpPermission.READ_REPOSITORY));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ public DistributionGrid(final CommonUiDependencies uiDependencies, final TargetM
DsManagementFilterParams::new, getSelectionSupport()::deselectAll));
initFilterMappings();
getFilterSupport().setFilter(new DsManagementFilterParams());
this.invalidateDistributionSetSupport = new InvalidateDistributionSetSupport(this, i18n, notification,
dsInvalidationManagement);
this.invalidateDistributionSetSupport = new InvalidateDistributionSetSupport(this, i18n, uiProperties,
notification, permissionChecker, dsInvalidationManagement);

initStyleGenerator();
init();
Expand Down Expand Up @@ -239,7 +239,7 @@ private Column<ProxyDistributionSet, Button> addInvalidateColumn() {
i18n, clickEvent -> invalidateDistributionSetSupport.openConsequencesWindowOnInvalidateAction(ds),
VaadinIcons.BAN, UIMessageIdProvider.TOOLTIP_INVALIDATE_DISTRIBUTIONSET,
SPUIStyleDefinitions.STATUS_ICON_NEUTRAL, UIComponentIdProvider.DIST_INVALIDATE_ICON + "." + ds.getId(),
ds.getIsValid());
ds.getIsValid() && permissionChecker.hasDistributionSetInvalidatePermission());
return GridComponentBuilder.addIconColumn(this, buttonProvider, DS_INVALIDATE_BUTTON_ID, null);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@
import java.util.List;
import java.util.stream.Collectors;

import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.DistributionSetInvalidationManagement;
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidationCount;
import org.eclipse.hawkbit.ui.SpPermissionChecker;
import org.eclipse.hawkbit.ui.UiProperties;
import org.eclipse.hawkbit.ui.common.data.proxies.ProxyDistributionSet;
import org.eclipse.hawkbit.ui.utils.UIMessageIdProvider;
import org.eclipse.hawkbit.ui.utils.UINotification;
Expand All @@ -35,7 +38,10 @@ public class InvalidateDistributionSetSupport {
private static final Logger LOG = LoggerFactory.getLogger(InvalidateDistributionSetSupport.class);

private final VaadinMessageSource i18n;
private final UiProperties uiProperties;
private final UINotification notification;
private final SpPermissionChecker permissionChecker;

private final DistributionGrid grid;

private final DistributionSetInvalidationManagement dsInvalidationManagement;
Expand All @@ -55,10 +61,14 @@ public class InvalidateDistributionSetSupport {
* {@link DistributionSetInvalidationManagement}
*/
public InvalidateDistributionSetSupport(final DistributionGrid grid, final VaadinMessageSource i18n,
final UINotification notification, final DistributionSetInvalidationManagement dsInvalidationManagement) {
final UiProperties uiProperties, final UINotification notification,
final SpPermissionChecker permissionChecker,
final DistributionSetInvalidationManagement dsInvalidationManagement) {
this.grid = grid;
this.i18n = i18n;
this.uiProperties = uiProperties;
this.notification = notification;
this.permissionChecker = permissionChecker;
this.dsInvalidationManagement = dsInvalidationManagement;
}

Expand All @@ -72,11 +82,12 @@ public void openConsequencesWindowOnInvalidateAction(final ProxyDistributionSet
final List<ProxyDistributionSet> allDistributionSetsForInvalidation = getDistributionSetsForInvalidation(
clickedDistributionSet);

consequencesDialog = new InvalidateDsConsequencesDialog(allDistributionSetsForInvalidation, i18n, ok -> {
if (ok) {
openAffectedEntitiesWindowOnInvalidateAction(allDistributionSetsForInvalidation);
}
});
consequencesDialog = new InvalidateDsConsequencesDialog(allDistributionSetsForInvalidation, i18n, uiProperties,
ok -> {
if (Boolean.TRUE.equals(ok) && hasSufficientPermission()) {
openAffectedEntitiesWindowOnInvalidateAction(allDistributionSetsForInvalidation);
}
});
consequencesDialog.getWindow().setWidth(40.0F, Sizeable.Unit.PERCENTAGE);

UI.getCurrent().addWindow(consequencesDialog.getWindow());
Expand All @@ -89,15 +100,15 @@ private void openAffectedEntitiesWindowOnInvalidateAction(
final DistributionSetInvalidationCount entitiesForInvalidationCount = dsInvalidationManagement
.countEntitiesForInvalidation(
getDistributionSetInvalidation(consequencesDialog.isStopRolloutsSelected(),
getDistributionSetIds(allDistributionSetsForInvalidation), CancelationType.NONE));
getDistributionSetIds(allDistributionSetsForInvalidation),
consequencesDialog.getCancelationType()));

final InvalidateDsAffectedEntitiesDialog affectedEntitiesDialog = new InvalidateDsAffectedEntitiesDialog(
allDistributionSetsForInvalidation, i18n, ok -> {
if (ok) {
if (Boolean.TRUE.equals(ok) && hasSufficientPermission()) {
handleOkForInvalidateDistributionSet(allDistributionSetsForInvalidation);
}
}, entitiesForInvalidationCount.getRolloutsCount(),
entitiesForInvalidationCount.getAutoAssignmentCount());
}, entitiesForInvalidationCount);
affectedEntitiesDialog.getWindow().setWidth(40.0F, Sizeable.Unit.PERCENTAGE);

UI.getCurrent().addWindow(affectedEntitiesDialog.getWindow());
Expand All @@ -106,11 +117,11 @@ private void openAffectedEntitiesWindowOnInvalidateAction(

private void handleOkForInvalidateDistributionSet(
final List<ProxyDistributionSet> allDistributionSetsForInvalidation) {

try {
dsInvalidationManagement.invalidateDistributionSet(
getDistributionSetInvalidation(consequencesDialog.isStopRolloutsSelected(),
getDistributionSetIds(allDistributionSetsForInvalidation), CancelationType.NONE));
getDistributionSetIds(allDistributionSetsForInvalidation),
consequencesDialog.getCancelationType()));
notification.displaySuccess(createSuccessNotificationText(allDistributionSetsForInvalidation));
grid.refreshAll();
} catch (final RuntimeException ex) {
Expand All @@ -121,6 +132,21 @@ private void handleOkForInvalidateDistributionSet(
}
}

private boolean hasSufficientPermission() {
if (consequencesDialog.isStopRolloutsSelected() && !permissionChecker.hasRolloutUpdatePermission()) {
notification.displayValidationError(i18n.getMessage(
UIMessageIdProvider.MESSAGE_ERROR_PERMISSION_INSUFFICIENT, SpPermission.UPDATE_ROLLOUT));
return false;
}
if (consequencesDialog.getCancelationType() != CancelationType.NONE
&& !permissionChecker.hasUpdateTargetPermission()) {
notification.displayValidationError(i18n
.getMessage(UIMessageIdProvider.MESSAGE_ERROR_PERMISSION_INSUFFICIENT, SpPermission.UPDATE_TARGET));
return false;
}
return true;
}

private DistributionSetInvalidation getDistributionSetInvalidation(final boolean stopRollouts,
final List<Long> distSetIds, final CancelationType cancelationType) {
return new DistributionSetInvalidation(distSetIds, cancelationType, stopRollouts);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
import java.util.List;
import java.util.function.Consumer;

import org.eclipse.hawkbit.repository.model.DistributionSetInvalidationCount;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.ui.common.CommonDialogWindow;
import org.eclipse.hawkbit.ui.common.CommonDialogWindow.ConfirmStyle;
Expand Down Expand Up @@ -44,20 +46,19 @@ public class InvalidateDsAffectedEntitiesDialog {
* Constructor for {@link InvalidateDsAffectedEntitiesDialog}
*
* @param allDistributionSetsForInvalidation
* {@link List} of {@link ProxyDistributionSet} that are selected
* for invalidation
* {@link List} of {@link ProxyDistributionSet} that are selected for
* invalidation
* @param i18n
* {@link VaadinMessageSource}
* @param callback
* callback for dialog result
* @param affectedRollouts
* number of affected {@link Rollout}s
* @param affectedAutoAssignments
* number of affected auto assignments
* @param affectedEntities
* number of affected {@link Rollout}s, {@link Action}s and
* auto-assignments
*/
public InvalidateDsAffectedEntitiesDialog(final List<ProxyDistributionSet> allDistributionSetsForInvalidation,
final VaadinMessageSource i18n, final Consumer<Boolean> callback, final long affectedRollouts,
final long affectedAutoAssignments) {
final VaadinMessageSource i18n, final Consumer<Boolean> callback,
final DistributionSetInvalidationCount affectedEntities) {

this.i18n = i18n;

Expand All @@ -69,14 +70,21 @@ public InvalidateDsAffectedEntitiesDialog(final List<ProxyDistributionSet> allDi
consequencesLabel.setWidthFull();
content.addComponent(consequencesLabel);

final Label stoppedRolloutsLabel = new Label(i18n.getMessage(
UIMessageIdProvider.MESSAGE_INVALIDATE_DISTRIBUTIONSET_AFFECTED_ENTITIES_ROLLOUTS, affectedRollouts));
final Label stoppedSingleAssignmentsLabel = new Label(
i18n.getMessage(UIMessageIdProvider.MESSAGE_INVALIDATE_DISTRIBUTIONSET_AFFECTED_ENTITIES_ACTIONS,
affectedEntities.getActionCount()));
stoppedSingleAssignmentsLabel.setId(UIComponentIdProvider.INVALIDATE_DS_AFFECTED_ENTITIES_ACTIONS);
content.addComponent(stoppedSingleAssignmentsLabel);

final Label stoppedRolloutsLabel = new Label(
i18n.getMessage(UIMessageIdProvider.MESSAGE_INVALIDATE_DISTRIBUTIONSET_AFFECTED_ENTITIES_ROLLOUTS,
affectedEntities.getRolloutsCount()));
stoppedRolloutsLabel.setId(UIComponentIdProvider.INVALIDATE_DS_AFFECTED_ENTITIES_ROLLOUTS);
content.addComponent(stoppedRolloutsLabel);

final Label stoppedAutoAssignmentsLabel = new Label(i18n.getMessage(
UIMessageIdProvider.MESSAGE_INVALIDATE_DISTRIBUTIONSET_AFFECTED_ENTITIES_AUTOASSIGNMENTS,
affectedAutoAssignments));
affectedEntities.getAutoAssignmentCount()));
stoppedAutoAssignmentsLabel.setId(UIComponentIdProvider.INVALIDATE_DS_AFFECTED_ENTITIES_AUTOASSIGNMENTS);
content.addComponent(stoppedAutoAssignmentsLabel);

Expand Down
Loading