Feature download only (#810)
* Added initial version of DOWNLOAD_ONLY * Added DOWNLOAD_ONLY option to ActionTypeOptionGroupLayout * Removed DOWNLOAD_ONLY checkbox, added Download Only UI option * Mark actions that finished with DOWNLOADED as finished * initial changes to realize downoadOnly in UI * Changed method of disabling maintenanceWindow into smarter solution * Added new icon for download only option * Set DistributionSet as unassigned when DOWNLOAD_ONLY * Enabled update action status for DOWNLOAD_ONLY after download * Current state of abstraction task * Assign DistributionSet to target if target installs it after downloading * Abstracted class redundant methods * Added tests * Fixed Rollout finish status for DWONLOAD_ONLY Rollouts * Added Rollout type json property in test documentation * Added DOWNLOAD_ONLY test for target assignment * Added event listener also to DistributionTable * Fixed event listener problem * Change column name to "Type" and added also DownloadOnly icon to that column. * Cleanup * Center aligned the icons in type column * Fixed DistributionSet already assigned but not installed * Rename download_only to downloadonly * Further changes regarding center aligned the icons * Fixed target assign status in Rollout view when download_only * Fixed SonarQube issues * Fixed SonarQube issues + code formatting * Fixed Tests * Marked squid:S128 as suppressed - irrelevant * Adapting rollouts view by additional column (not finished by now) * Putted type column on proper position * Trying to display icons in new type column in rollouts view * Added icon also for soft, icon might change -> just change * createOptionGroup method in ActionTypeOptionGroupLayout class * added first draft of type column in rollouts view * increase visibility of sendUpdateMessageToTarget method * Ground functionality of new type column in deployment view is now implemented * Type column implementation in rollouts view is finished for now * Rebased on master * Fixed DurationControl change on ScheduleControl change. * (Re)Added Soft deployment Icon * Fixed SonarQube issues * Fixed SonarQube issues * Fixed failing test * Fixes + added missing header * Added message to the fail() instruction * Fixed copyright header * Apply suggestions from code review * Fixed TotalTargetCountStatus.java * Removed unused method from TotalTargetCountStatus.java * add id to rollout create and update UI popup * Added download_only tests for MgmtTargetResourceTest.java * added missing header in TotalTargetCountStatusTest.java * Rename because of newest changes * added Download_Only dmf integration tests * Renamed MgmtAction.forcedType to actionType * renamed actionType to forceType for Mgmt API * added missing javadocs for public methods * Added Download Only support for AutoAssignment Signed-off-by: Ahmed Sayed <ahmed.sayed@bosch-si.com> Signed-off-by: Ammar Bikic <ammar.bikic@bosch-si.com>
This commit is contained in:
@@ -23,9 +23,12 @@ public class RepositoryProperties {
|
||||
|
||||
/**
|
||||
* Set to <code>true</code> if the repository has to reject
|
||||
* {@link ActionStatus} entries for actions that are closed. Note: if this
|
||||
* is enforced you have to make sure that the feedback channel from the
|
||||
* devices i in order.
|
||||
* {@link ActionStatus} entries for actions that are closed. This is
|
||||
* especially useful if the action status feedback channel order from the
|
||||
* device cannot be guaranteed.
|
||||
*
|
||||
* Note: if this is enforced you have to make sure that the feedback
|
||||
* channel from the devices is in order.
|
||||
*/
|
||||
private boolean rejectActionStatusForClosedAction;
|
||||
|
||||
|
||||
@@ -8,13 +8,14 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.event.remote;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.ActionProperties;
|
||||
|
||||
/**
|
||||
* TenantAwareEvent that gets sent when a distribution set gets assigned to a
|
||||
@@ -28,7 +29,7 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent {
|
||||
|
||||
private boolean maintenanceWindowAvailable;
|
||||
|
||||
private final Map<String, Long> actions = new HashMap<>();
|
||||
private final Map<String, ActionProperties> actions = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
@@ -57,12 +58,20 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent {
|
||||
this.distributionSetId = distributionSetId;
|
||||
this.maintenanceWindowAvailable = maintenanceWindowAvailable;
|
||||
actions.putAll(a.stream().filter(action -> action.getDistributionSet().getId().longValue() == distributionSetId)
|
||||
.collect(Collectors.toMap(action -> action.getTarget().getControllerId(), Action::getId)));
|
||||
.collect(Collectors.toMap(action -> action.getTarget().getControllerId(), ActionProperties::new)));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param action
|
||||
* the action created for this assignment
|
||||
* @param applicationId
|
||||
* the application id
|
||||
*/
|
||||
public TargetAssignDistributionSetEvent(final Action action, final String applicationId) {
|
||||
this(action.getTenant(), action.getDistributionSet().getId(), Arrays.asList(action), applicationId,
|
||||
this(action.getTenant(), action.getDistributionSet().getId(), Collections.singletonList(action), applicationId,
|
||||
action.isMaintenanceWindowAvailable());
|
||||
}
|
||||
|
||||
@@ -74,7 +83,7 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent {
|
||||
return maintenanceWindowAvailable;
|
||||
}
|
||||
|
||||
public Map<String, Long> getActions() {
|
||||
public Map<String, ActionProperties> getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
|
||||
@@ -229,7 +229,12 @@ public interface Action extends TenantAwareBaseEntity {
|
||||
* {@link Action#isHitAutoForceTime(long)} is reached, {@link #FORCED}
|
||||
* after that.
|
||||
*/
|
||||
TIMEFORCED;
|
||||
TIMEFORCED,
|
||||
|
||||
/**
|
||||
* Target is only advised to download, but not install
|
||||
*/
|
||||
DOWNLOAD_ONLY;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 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.io.Serializable;
|
||||
|
||||
/**
|
||||
* Holds properties for {@link Action}
|
||||
*/
|
||||
public class ActionProperties implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
private Action.ActionType actionType;
|
||||
private String tenant;
|
||||
private boolean maintenanceWindowAvailable;
|
||||
|
||||
public ActionProperties() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param action
|
||||
* the action to populate the properties from
|
||||
*/
|
||||
public ActionProperties(final Action action) {
|
||||
this.id = action.getId();
|
||||
this.actionType = action.getActionType();
|
||||
this.tenant = action.getTenant();
|
||||
this.maintenanceWindowAvailable = action.isMaintenanceWindowAvailable();
|
||||
}
|
||||
|
||||
public void setId(final Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setTenant(final String tenant) {
|
||||
this.tenant = tenant;
|
||||
}
|
||||
|
||||
public String getTenant() {
|
||||
return tenant;
|
||||
}
|
||||
|
||||
public void setMaintenanceWindowAvailable(final boolean maintenanceWindowAvailable) {
|
||||
this.maintenanceWindowAvailable = maintenanceWindowAvailable;
|
||||
}
|
||||
|
||||
public boolean isMaintenanceWindowAvailable() {
|
||||
return maintenanceWindowAvailable;
|
||||
}
|
||||
|
||||
public Action.ActionType getActionType() {
|
||||
return actionType;
|
||||
}
|
||||
|
||||
public void setActionType(final Action.ActionType actionType) {
|
||||
this.actionType = actionType;
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ public interface TargetFilterQuery extends TenantAwareBaseEntity {
|
||||
* Allowed values for auto-assign action type
|
||||
*/
|
||||
Set<ActionType> ALLOWED_AUTO_ASSIGN_ACTION_TYPES = Collections
|
||||
.unmodifiableSet(EnumSet.of(ActionType.FORCED, ActionType.SOFT));
|
||||
.unmodifiableSet(EnumSet.of(ActionType.FORCED, ActionType.SOFT, ActionType.DOWNLOAD_ONLY));
|
||||
|
||||
/**
|
||||
* @return name of the {@link TargetFilterQuery}.
|
||||
|
||||
@@ -58,6 +58,7 @@ public class TotalTargetCountStatus {
|
||||
|
||||
private final Map<Status, Long> statusTotalCountMap = new EnumMap<>(Status.class);
|
||||
private final Long totalTargetCount;
|
||||
private final Action.ActionType rolloutType;
|
||||
|
||||
/**
|
||||
* Create a new states map with the target count for each state.
|
||||
@@ -66,11 +67,14 @@ public class TotalTargetCountStatus {
|
||||
* the action state map
|
||||
* @param totalTargetCount
|
||||
* the total target count
|
||||
* @param rolloutType
|
||||
* the type of the rollout
|
||||
*/
|
||||
public TotalTargetCountStatus(final List<TotalTargetCountActionStatus> targetCountActionStatus,
|
||||
final Long totalTargetCount) {
|
||||
final Long totalTargetCount, final Action.ActionType rolloutType) {
|
||||
this.totalTargetCount = totalTargetCount;
|
||||
mapActionStatusToTotalTargetCountStatus(targetCountActionStatus);
|
||||
this.rolloutType = rolloutType;
|
||||
addToTotalCount(targetCountActionStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,9 +82,11 @@ public class TotalTargetCountStatus {
|
||||
*
|
||||
* @param totalTargetCount
|
||||
* the total target count
|
||||
* @param rolloutType
|
||||
* the type of the rollout
|
||||
*/
|
||||
public TotalTargetCountStatus(final Long totalTargetCount) {
|
||||
this(Collections.emptyList(), totalTargetCount);
|
||||
public TotalTargetCountStatus(final Long totalTargetCount, final Action.ActionType rolloutType) {
|
||||
this(Collections.emptyList(), totalTargetCount, rolloutType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,8 +125,7 @@ public class TotalTargetCountStatus {
|
||||
* @param rolloutStatusCountItems
|
||||
* all target {@link Status} with total count
|
||||
*/
|
||||
private final void mapActionStatusToTotalTargetCountStatus(
|
||||
final List<TotalTargetCountActionStatus> targetCountActionStatus) {
|
||||
private void addToTotalCount(final List<TotalTargetCountActionStatus> targetCountActionStatus) {
|
||||
if (targetCountActionStatus == null) {
|
||||
statusTotalCountMap.put(TotalTargetCountStatus.Status.NOTSTARTED, totalTargetCount);
|
||||
return;
|
||||
@@ -128,40 +133,40 @@ public class TotalTargetCountStatus {
|
||||
statusTotalCountMap.put(Status.RUNNING, 0L);
|
||||
Long notStartedTargetCount = totalTargetCount;
|
||||
for (final TotalTargetCountActionStatus item : targetCountActionStatus) {
|
||||
convertStatus(item);
|
||||
addToTotalCount(item);
|
||||
notStartedTargetCount -= item.getCount();
|
||||
}
|
||||
statusTotalCountMap.put(TotalTargetCountStatus.Status.NOTSTARTED, notStartedTargetCount);
|
||||
}
|
||||
|
||||
private void addToTotalCount(final TotalTargetCountActionStatus item) {
|
||||
final Status status = convertStatus(item.getStatus());
|
||||
statusTotalCountMap.merge(status, item.getCount(), Long::sum);
|
||||
}
|
||||
|
||||
// Exception squid:MethodCyclomaticComplexity - simple state conversion, not
|
||||
// really complex.
|
||||
@SuppressWarnings("squid:MethodCyclomaticComplexity")
|
||||
private void convertStatus(final TotalTargetCountActionStatus item) {
|
||||
switch (item.getStatus()) {
|
||||
private Status convertStatus(final Action.Status status){
|
||||
switch (status) {
|
||||
case SCHEDULED:
|
||||
statusTotalCountMap.put(Status.SCHEDULED, item.getCount());
|
||||
break;
|
||||
return Status.SCHEDULED;
|
||||
case ERROR:
|
||||
statusTotalCountMap.put(Status.ERROR, item.getCount());
|
||||
break;
|
||||
return Status.ERROR;
|
||||
case FINISHED:
|
||||
statusTotalCountMap.put(Status.FINISHED, item.getCount());
|
||||
break;
|
||||
return Status.FINISHED;
|
||||
case CANCELED:
|
||||
return Status.CANCELLED;
|
||||
case RETRIEVED:
|
||||
case RUNNING:
|
||||
case WARNING:
|
||||
case DOWNLOAD:
|
||||
case DOWNLOADED:
|
||||
case CANCELING:
|
||||
final Long runningItemsCount = statusTotalCountMap.get(Status.RUNNING) + item.getCount();
|
||||
statusTotalCountMap.put(Status.RUNNING, runningItemsCount);
|
||||
break;
|
||||
case CANCELED:
|
||||
statusTotalCountMap.put(Status.CANCELLED, item.getCount());
|
||||
break;
|
||||
return Status.RUNNING;
|
||||
case DOWNLOADED:
|
||||
return Action.ActionType.DOWNLOAD_ONLY.equals(rolloutType) ? Status.FINISHED : Status.RUNNING;
|
||||
default:
|
||||
throw new IllegalArgumentException("State " + item.getStatus() + "is not valid");
|
||||
throw new IllegalArgumentException("State " + status + "is not valid");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 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 io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@Feature("Component Tests - TotalTargetCountStatus")
|
||||
@Story("TotalTargetCountStatus should correctly present finished DOWNLOAD_ONLY actions")
|
||||
public class TotalTargetCountStatusTest {
|
||||
|
||||
private final List<TotalTargetCountActionStatus> targetCountActionStatuses = Arrays.asList(
|
||||
new TotalTargetCountActionStatus(Action.Status.SCHEDULED, 1L),
|
||||
new TotalTargetCountActionStatus(Action.Status.ERROR, 2L),
|
||||
new TotalTargetCountActionStatus(Action.Status.FINISHED, 3L),
|
||||
new TotalTargetCountActionStatus(Action.Status.CANCELED, 4L),
|
||||
new TotalTargetCountActionStatus(Action.Status.RETRIEVED, 5L),
|
||||
new TotalTargetCountActionStatus(Action.Status.RUNNING, 6L),
|
||||
new TotalTargetCountActionStatus(Action.Status.WARNING, 7L),
|
||||
new TotalTargetCountActionStatus(Action.Status.DOWNLOAD, 8L),
|
||||
new TotalTargetCountActionStatus(Action.Status.CANCELING, 9L),
|
||||
new TotalTargetCountActionStatus(Action.Status.DOWNLOADED, 10L));
|
||||
|
||||
@Test
|
||||
@Description("Different Action Statuses should be correctly mapped to the corresponding " +
|
||||
"TotalTargetCountStatus.Status")
|
||||
public void shouldCorrectlyMapActionStatuses() {
|
||||
TotalTargetCountStatus status = new TotalTargetCountStatus(targetCountActionStatuses, 55L,
|
||||
Action.ActionType.FORCED);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.SCHEDULED)).isEqualTo(1L);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.ERROR)).isEqualTo(2L);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.FINISHED)).isEqualTo(3L);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.CANCELLED)).isEqualTo(4L);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.RUNNING)).isEqualTo(45L);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.NOTSTARTED)).isEqualTo(0L);
|
||||
assertThat(status.getFinishedPercent()).isEqualTo((float) 100 * 3 / 55);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("When an empty list is passed to the TotalTargetCountStatus, all actions should be displayed as " +
|
||||
"NOTSTARTED")
|
||||
public void shouldCorrectlyMapActionStatusesToNotStarted() {
|
||||
TotalTargetCountStatus status = new TotalTargetCountStatus(Collections.emptyList(), 55L,
|
||||
Action.ActionType.FORCED);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.SCHEDULED)).isEqualTo(0L);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.ERROR)).isEqualTo(0L);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.FINISHED)).isEqualTo(0L);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.CANCELLED)).isEqualTo(0L);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.RUNNING)).isEqualTo(0L);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.NOTSTARTED)).isEqualTo(55L);
|
||||
assertThat(status.getFinishedPercent()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("DownloadOnly actions should be displayed as FINISHED when they have ActionStatus.DOWNLOADED")
|
||||
public void shouldCorrectlyMapActionStatusesInDownloadOnlyCase() {
|
||||
TotalTargetCountStatus status = new TotalTargetCountStatus(targetCountActionStatuses, 55L,
|
||||
Action.ActionType.DOWNLOAD_ONLY);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.SCHEDULED)).isEqualTo(1L);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.ERROR)).isEqualTo(2L);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.FINISHED)).isEqualTo(13L);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.CANCELLED)).isEqualTo(4L);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.RUNNING)).isEqualTo(35L);
|
||||
assertThat(status.getTotalTargetCountByStatus(TotalTargetCountStatus.Status.NOTSTARTED)).isEqualTo(0L);
|
||||
assertThat(status.getFinishedPercent()).isEqualTo((float) 100 * 13 / 55);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user