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:
Ahmed Sayed
2019-04-17 12:27:23 +02:00
committed by Stefan Behl
parent 04b9abda3b
commit ed95ae6398
76 changed files with 1754 additions and 639 deletions

View File

@@ -313,17 +313,13 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
* the rollout the actions are belong to
* @param rolloutGroup
* the rolloutgroup the actions are belong to
* @param notStatus1
* the status the action should not have
* @param notStatus2
* the status the action should not have
* @param notStatus3
* the status the action should not have
* @param statuses
* the list of statuses the action should not have
* @return the count of actions referring the rollout and rolloutgroup and
* are not in given states
*/
Long countByRolloutAndRolloutGroupAndStatusNotAndStatusNotAndStatusNot(JpaRollout rollout,
JpaRolloutGroup rolloutGroup, Status notStatus1, Status notStatus2, Status notStatus3);
Long countByRolloutAndRolloutGroupAndStatusNotIn(JpaRollout rollout, JpaRolloutGroup rolloutGroup,
List<Status> statuses);
/**
* Counts all actions referring to a given rollout and rolloutgroup.

View File

@@ -8,6 +8,9 @@
*/
package org.eclipse.hawkbit.repository.jpa;
import static org.eclipse.hawkbit.repository.model.Action.ActionType.DOWNLOAD_ONLY;
import static org.eclipse.hawkbit.repository.model.Action.Status.DOWNLOADED;
import static org.eclipse.hawkbit.repository.model.Action.Status.FINISHED;
import static org.eclipse.hawkbit.repository.model.Target.CONTROLLER_ATTRIBUTE_KEY_SIZE;
import static org.eclipse.hawkbit.repository.model.Target.CONTROLLER_ATTRIBUTE_VALUE_SIZE;
@@ -452,7 +455,7 @@ public class JpaControllerManagement implements ControllerManagement {
"UPDATE sp_target SET last_target_query = #last_target_query WHERE controller_id IN ("
+ formatQueryInStatementParams(paramMapping.keySet()) + ") AND tenant = #tenant");
paramMapping.entrySet().forEach(entry -> updateQuery.setParameter(entry.getKey(), entry.getValue()));
paramMapping.forEach(updateQuery::setParameter);
updateQuery.setParameter("last_target_query", currentTimeMillis);
updateQuery.setParameter("tenant", tenant);
@@ -561,23 +564,37 @@ public class JpaControllerManagement implements ControllerManagement {
final JpaAction action = getActionAndThrowExceptionIfNotFound(create.getActionId());
final JpaActionStatus actionStatus = create.build();
// if action is already closed we accept further status updates if
// permitted so by configuration. This is especially useful if the
// action status feedback channel order from the device cannot be
// guaranteed. However, if an action is closed we do not accept further
// close messages.
if (actionIsNotActiveButIntermediateFeedbackStillAllowed(actionStatus, action.isActive())) {
LOG.debug("Update of actionStatus {} for action {} not possible since action not active anymore.",
actionStatus.getStatus(), action.getId());
return action;
if (isUpdatingActionStatusAllowed(action, actionStatus)) {
return handleAddUpdateActionStatus(actionStatus, action);
}
return handleAddUpdateActionStatus(actionStatus, action);
LOG.debug("Update of actionStatus {} for action {} not possible since action not active anymore.",
actionStatus.getStatus(), action.getId());
return action;
}
private boolean actionIsNotActiveButIntermediateFeedbackStillAllowed(final ActionStatus actionStatus,
final boolean actionActive) {
return !actionActive && (repositoryProperties.isRejectActionStatusForClosedAction()
|| Status.ERROR.equals(actionStatus.getStatus()) || Status.FINISHED.equals(actionStatus.getStatus()));
/**
* ActionStatus updates are allowed mainly if the action is active. If the
* action is not active we accept further status updates if permitted so
* by repository configuration. In this case, only the values: Status.ERROR
* and Status.FINISHED are allowed. In the case of a DOWNLOAD_ONLY action,
* we accept status updates only once.
*/
private boolean isUpdatingActionStatusAllowed(final JpaAction action, final JpaActionStatus actionStatus) {
final boolean isIntermediateFeedback = !FINISHED.equals(actionStatus.getStatus())
&& !Status.ERROR.equals(actionStatus.getStatus());
final boolean isAllowedByRepositoryConfiguration = !repositoryProperties.isRejectActionStatusForClosedAction()
&& isIntermediateFeedback;
final boolean isAllowedForDownloadOnlyActions = isDownloadOnly(action) && !isIntermediateFeedback;
return action.isActive() || isAllowedByRepositoryConfiguration || isAllowedForDownloadOnlyActions;
}
private static boolean isDownloadOnly(final JpaAction action) {
return DOWNLOAD_ONLY.equals(action.getActionType());
}
/**
@@ -588,6 +605,10 @@ public class JpaControllerManagement implements ControllerManagement {
String controllerId = null;
LOG.debug("handleAddUpdateActionStatus for action {}", action.getId());
// information status entry - check for a potential DOS attack
assertActionStatusQuota(action);
assertActionStatusMessageQuota(actionStatus);
switch (actionStatus.getStatus()) {
case ERROR:
final JpaTarget target = (JpaTarget) action.getTarget();
@@ -597,10 +618,10 @@ public class JpaControllerManagement implements ControllerManagement {
case FINISHED:
controllerId = handleFinishedAndStoreInTargetStatus(action);
break;
case DOWNLOADED:
controllerId = handleDownloadedActionStatus(action);
break;
default:
// information status entry - check for a potential DOS attack
assertActionStatusQuota(action);
assertActionStatusMessageQuota(actionStatus);
break;
}
@@ -615,6 +636,20 @@ public class JpaControllerManagement implements ControllerManagement {
return savedAction;
}
private String handleDownloadedActionStatus(final JpaAction action) {
if(!isDownloadOnly(action)){
return null;
}
JpaTarget target = (JpaTarget) action.getTarget();
action.setActive(false);
action.setStatus(DOWNLOADED);
target.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
targetRepository.save(target);
return target.getControllerId();
}
private void requestControllerAttributes(final String controllerId) {
final JpaTarget target = (JpaTarget) getByControllerId(controllerId)
.orElseThrow(() -> new EntityNotFoundException(Target.class, controllerId));
@@ -648,6 +683,12 @@ public class JpaControllerManagement implements ControllerManagement {
target.setInstalledDistributionSet(ds);
target.setInstallationDate(System.currentTimeMillis());
// Target reported an installation of a DOWNLOAD_ONLY assignment, the assigned DS has to be adapted
// because the currently assigned DS can be unequal to the currently installed DS (the downloadOnly DS)
if(isDownloadOnly(action)){
target.setAssignedDistributionSet(action.getDistributionSet());
}
// check if the assigned set is equal to the installed set (not
// necessarily the case as another update might be pending already).
if (target.getAssignedDistributionSet() != null

View File

@@ -151,7 +151,8 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
for (final JpaRolloutGroup rolloutGroup : rolloutGroups) {
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
allStatesForRollout.get(rolloutGroup.getId()), Long.valueOf(rolloutGroup.getTotalTargets()));
allStatesForRollout.get(rolloutGroup.getId()), Long.valueOf(rolloutGroup.getTotalTargets()),
rolloutGroup.getRollout().getActionType());
rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus);
}
@@ -177,7 +178,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
}
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
Long.valueOf(jpaRolloutGroup.getTotalTargets()));
Long.valueOf(jpaRolloutGroup.getTotalTargets()), jpaRolloutGroup.getRollout().getActionType());
jpaRolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus);
return rolloutGroup;

View File

@@ -105,6 +105,8 @@ import org.springframework.validation.annotation.Validated;
import com.google.common.collect.Lists;
import static org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate.addSuccessAndErrorConditionsAndActions;
/**
* JPA implementation of {@link RolloutManagement}.
*/
@@ -126,6 +128,12 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
private static final List<RolloutStatus> ACTIVE_ROLLOUTS = Arrays.asList(RolloutStatus.CREATING,
RolloutStatus.DELETING, RolloutStatus.STARTING, RolloutStatus.READY, RolloutStatus.RUNNING);
// In case of DOWNLOAD_ONLY, actions can be finished with DOWNLOADED status.
private static final List<Status> DOWNLOAD_ONLY_ACTION_TERMINATION_STATUSES = Arrays.asList(Status.ERROR,
Status.FINISHED, Status.CANCELED, Status.DOWNLOADED);
private static final List<Status> DEFAULT_ACTION_TERMINATION_STATUSES = Arrays.asList(Status.ERROR, Status.FINISHED,
Status.CANCELED);
@Autowired
private RolloutRepository rolloutRepository;
@@ -251,17 +259,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
group.setParent(lastSavedGroup);
group.setStatus(RolloutGroupStatus.CREATING);
group.setSuccessCondition(conditions.getSuccessCondition());
group.setSuccessConditionExp(conditions.getSuccessConditionExp());
group.setSuccessAction(conditions.getSuccessAction());
group.setSuccessActionExp(conditions.getSuccessActionExp());
group.setErrorCondition(conditions.getErrorCondition());
group.setErrorConditionExp(conditions.getErrorConditionExp());
group.setErrorAction(conditions.getErrorAction());
group.setErrorActionExp(conditions.getErrorActionExp());
addSuccessAndErrorConditionsAndActions(group, conditions);
group.setTargetPercentage(1.0F / (amountOfGroups - i) * 100);
@@ -310,17 +308,10 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
group.setTargetFilterQuery("");
}
group.setSuccessCondition(srcGroup.getSuccessCondition());
group.setSuccessConditionExp(srcGroup.getSuccessConditionExp());
group.setSuccessAction(srcGroup.getSuccessAction());
group.setSuccessActionExp(srcGroup.getSuccessActionExp());
group.setErrorCondition(srcGroup.getErrorCondition());
group.setErrorConditionExp(srcGroup.getErrorConditionExp());
group.setErrorAction(srcGroup.getErrorAction());
group.setErrorActionExp(srcGroup.getErrorActionExp());
addSuccessAndErrorConditionsAndActions(group, srcGroup.getSuccessCondition(),
srcGroup.getSuccessConditionExp(), srcGroup.getSuccessAction(), srcGroup.getSuccessActionExp(),
srcGroup.getErrorCondition(), srcGroup.getErrorConditionExp(), srcGroup.getErrorAction(),
srcGroup.getErrorActionExp());
lastSavedGroup = rolloutGroupRepository.save(group);
publishRolloutGroupCreatedEventAfterCommit(lastSavedGroup, rollout);
@@ -760,9 +751,11 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
}
private boolean isRolloutGroupComplete(final JpaRollout rollout, final JpaRolloutGroup rolloutGroup) {
final Long actionsLeftForRollout = actionRepository
.countByRolloutAndRolloutGroupAndStatusNotAndStatusNotAndStatusNot(rollout, rolloutGroup,
Action.Status.ERROR, Action.Status.FINISHED, Action.Status.CANCELED);
final Long actionsLeftForRollout = ActionType.DOWNLOAD_ONLY.equals(rollout.getActionType())
? actionRepository.countByRolloutAndRolloutGroupAndStatusNotIn(rollout, rolloutGroup,
DOWNLOAD_ONLY_ACTION_TERMINATION_STATUSES)
: actionRepository.countByRolloutAndRolloutGroupAndStatusNotIn(rollout, rolloutGroup,
DEFAULT_ACTION_TERMINATION_STATUSES);
return actionsLeftForRollout == 0;
}
@@ -1070,7 +1063,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
}
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(rolloutStatusCountItems,
rollout.get().getTotalTargets());
rollout.get().getTotalTargets(), rollout.get().getActionType());
((JpaRollout) rollout.get()).setTotalTargetCountStatus(totalTargetCountStatus);
return rollout;
}
@@ -1112,7 +1105,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
if (allStatesForRollout != null) {
rollouts.forEach(rollout -> {
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets());
allStatesForRollout.get(rollout.getId()), rollout.getTotalTargets(), rollout.getActionType());
rollout.setTotalTargetCountStatus(totalTargetCountStatus);
});
}

View File

@@ -11,6 +11,8 @@ package org.eclipse.hawkbit.repository.jpa.builder;
import org.eclipse.hawkbit.repository.builder.AbstractRolloutGroupCreate;
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
public class JpaRolloutGroupCreate extends AbstractRolloutGroupCreate<RolloutGroupCreate>
implements RolloutGroupCreate {
@@ -30,20 +32,66 @@ public class JpaRolloutGroupCreate extends AbstractRolloutGroupCreate<RolloutGro
group.setTargetPercentage(targetPercentage);
if (conditions != null) {
group.setSuccessCondition(conditions.getSuccessCondition());
group.setSuccessConditionExp(conditions.getSuccessConditionExp());
group.setSuccessAction(conditions.getSuccessAction());
group.setSuccessActionExp(conditions.getSuccessActionExp());
group.setErrorCondition(conditions.getErrorCondition());
group.setErrorConditionExp(conditions.getErrorConditionExp());
group.setErrorAction(conditions.getErrorAction());
group.setErrorActionExp(conditions.getErrorActionExp());
addSuccessAndErrorConditionsAndActions(group, conditions);
}
return group;
}
/**
* Set the Success And Error conditions for the rollout group
*
* @param group
* The Rollout group
* @param conditions
* The Rollout Success and Error Conditions
*/
public static void addSuccessAndErrorConditionsAndActions(final JpaRolloutGroup group,
final RolloutGroupConditions conditions) {
addSuccessAndErrorConditionsAndActions(group, conditions.getSuccessCondition(),
conditions.getSuccessConditionExp(), conditions.getSuccessAction(), conditions.getSuccessActionExp(),
conditions.getErrorCondition(), conditions.getErrorConditionExp(), conditions.getErrorAction(),
conditions.getErrorActionExp());
}
/**
* Set the Success And Error conditions for the rollout group
*
* @param group
* The Rollout group
* @param successCondition
* The Rollout group success condition
* @param successConditionExp
* The Rollout group success expression
* @param successAction
* The Rollout group success action
* @param successActionExp
* The Rollout group success action expression
* @param errorCondition
* The Rollout group error condition
* @param errorConditionExp
* The Rollout group error expression
* @param errorAction
* The Rollout group error action
* @param errorActionExp
* The Rollout group error action expression
*/
public static void addSuccessAndErrorConditionsAndActions(final JpaRolloutGroup group,
final RolloutGroup.RolloutGroupSuccessCondition successCondition, final String successConditionExp,
final RolloutGroup.RolloutGroupSuccessAction successAction, final String successActionExp,
final RolloutGroup.RolloutGroupErrorCondition errorCondition, final String errorConditionExp,
final RolloutGroup.RolloutGroupErrorAction errorAction, final String errorActionExp) {
group.setSuccessCondition(successCondition);
group.setSuccessConditionExp(successConditionExp);
group.setSuccessAction(successAction);
group.setSuccessActionExp(successActionExp);
group.setErrorCondition(errorCondition);
group.setErrorConditionExp(errorConditionExp);
group.setErrorAction(errorAction);
group.setErrorActionExp(errorActionExp);
}
}

View File

@@ -80,7 +80,8 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@ObjectTypeConverter(name = "actionType", objectType = Action.ActionType.class, dataType = Integer.class, conversionValues = {
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
@ConversionValue(objectValue = "SOFT", dataValue = "1"),
@ConversionValue(objectValue = "TIMEFORCED", dataValue = "2") })
@ConversionValue(objectValue = "TIMEFORCED", dataValue = "2"),
@ConversionValue(objectValue = "DOWNLOAD_ONLY", dataValue = "3") })
@Convert("actionType")
@NotNull
private ActionType actionType;

View File

@@ -102,7 +102,8 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
@ObjectTypeConverter(name = "actionType", objectType = Action.ActionType.class, dataType = Integer.class, conversionValues = {
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
@ConversionValue(objectValue = "SOFT", dataValue = "1"),
@ConversionValue(objectValue = "TIMEFORCED", dataValue = "2") })
@ConversionValue(objectValue = "TIMEFORCED", dataValue = "2"),
@ConversionValue(objectValue = "DOWNLOAD_ONLY", dataValue = "3") })
@Convert("actionType")
@NotNull
private ActionType actionType = ActionType.FORCED;
@@ -224,7 +225,7 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
@Override
public TotalTargetCountStatus getTotalTargetCountStatus() {
if (totalTargetCountStatus == null) {
totalTargetCountStatus = new TotalTargetCountStatus(totalTargets);
totalTargetCountStatus = new TotalTargetCountStatus(totalTargets, actionType);
}
return totalTargetCountStatus;
}

View File

@@ -261,7 +261,7 @@ public class JpaRolloutGroup extends AbstractJpaNamedEntity implements RolloutGr
@Override
public TotalTargetCountStatus getTotalTargetCountStatus() {
if (totalTargetCountStatus == null) {
totalTargetCountStatus = new TotalTargetCountStatus(Long.valueOf(totalTargets));
totalTargetCountStatus = new TotalTargetCountStatus(Long.valueOf(totalTargets), rollout.getActionType());
}
return totalTargetCountStatus;
}

View File

@@ -65,7 +65,9 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity
@Column(name = "auto_assign_action_type", nullable = true)
@ObjectTypeConverter(name = "autoAssignActionType", objectType = Action.ActionType.class, dataType = Integer.class, conversionValues = {
@ConversionValue(objectValue = "FORCED", dataValue = "0"),
@ConversionValue(objectValue = "SOFT", dataValue = "1") })
@ConversionValue(objectValue = "SOFT", dataValue = "1"),
// Conversion for 'TIMEFORCED' is disabled because it is not permitted in autoAssignment
@ConversionValue(objectValue = "DOWNLOAD_ONLY", dataValue = "3")})
@Convert("autoAssignActionType")
private ActionType autoAssignActionType;

View File

@@ -38,8 +38,10 @@ public class ThresholdRolloutGroupSuccessCondition implements RolloutGroupCondit
return true;
}
Action.Status completeActionStatus = Action.ActionType.DOWNLOAD_ONLY.equals(rollout.getActionType()) ?
Action.Status.DOWNLOADED : Action.Status.FINISHED;
final long finished = this.actionRepository.countByRolloutIdAndRolloutGroupIdAndStatus(rollout.getId(),
rolloutGroup.getId(), Action.Status.FINISHED);
rolloutGroup.getId(), completeActionStatus);
try {
final Integer threshold = Integer.valueOf(expression);
// calculate threshold