diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Action.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Action.java index aaf065464..c3ec5e424 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Action.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/Action.java @@ -135,6 +135,12 @@ public interface Action extends TenantAwareBaseEntity { */ String getInitiatedBy(); + /** + * @return the latest action status code. Performance optimization to not + * query the action status table for the last action status code. + */ + Optional getLastActionStatusCode(); + /** * checks if the {@link #getForcedTime()} is hit by the given * {@code hitTimeMillis}, by means if the given milliseconds are greater diff --git a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionStatus.java b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionStatus.java index efe59fea1..b2d5efa83 100644 --- a/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionStatus.java +++ b/hawkbit-repository/hawkbit-repository-api/src/main/java/org/eclipse/hawkbit/repository/model/TargetWithActionStatus.java @@ -12,7 +12,7 @@ import org.eclipse.hawkbit.repository.Identifiable; import org.eclipse.hawkbit.repository.model.Action.Status; /** - * + * * Target with action status. * */ @@ -22,6 +22,8 @@ public class TargetWithActionStatus implements Identifiable { private Status status; + private Integer lastActionStatusCode; + public TargetWithActionStatus(final Target target) { this.target = target; } @@ -31,6 +33,12 @@ public class TargetWithActionStatus implements Identifiable { this.target = target; } + public TargetWithActionStatus(final Target target, final Status status, final Integer lastActionStatusCode) { + this.status = status; + this.target = target; + this.lastActionStatusCode = lastActionStatusCode; + } + public Target getTarget() { return target; } @@ -52,4 +60,11 @@ public class TargetWithActionStatus implements Identifiable { return target.getId(); } + public Integer getLastActionStatusCode() { + return lastActionStatusCode; + } + + public void setLastActionStatusCode(final Integer lastActionStatusCode) { + this.lastActionStatusCode = lastActionStatusCode; + } } 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 e05d8d2cf..1fb17aea1 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 @@ -48,9 +48,9 @@ import org.eclipse.hawkbit.repository.RepositoryProperties; import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.UpdateMode; import org.eclipse.hawkbit.repository.builder.ActionStatusCreate; +import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent; import org.eclipse.hawkbit.repository.event.remote.TargetAttributesRequestedEvent; import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent; -import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent; import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException; import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException; import org.eclipse.hawkbit.repository.exception.EntityNotFoundException; @@ -650,6 +650,8 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont actionStatus.setAction(action); actionStatusRepository.save(actionStatus); + + action.setLastActionStatusCode(actionStatus.getCode().orElse(null)); final Action savedAction = actionRepository.save(action); if (controllerId != null) { diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java index 7a051c0ca..c0e728184 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/JpaRolloutGroupManagement.java @@ -22,6 +22,7 @@ import javax.persistence.criteria.Join; import javax.persistence.criteria.JoinType; import javax.persistence.criteria.ListJoin; import javax.persistence.criteria.Order; +import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.eclipse.hawkbit.repository.RolloutGroupFields; @@ -242,34 +243,37 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement { @Override public Page findAllTargetsOfRolloutGroupWithActionStatus(final Pageable pageRequest, final long rolloutGroupId) { + throwExceptionIfRolloutGroupDoesNotExist(rolloutGroupId); final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); final CriteriaQuery query = cb.createQuery(Object[].class); - final CriteriaQuery countQuery = cb.createQuery(Long.class); - final Root targetRoot = query.distinct(true).from(RolloutTargetGroup.class); final Join targetJoin = targetRoot.join(RolloutTargetGroup_.target); final ListJoin actionJoin = targetRoot.join(RolloutTargetGroup_.actions, JoinType.LEFT); - final Root countQueryFrom = countQuery.distinct(true).from(RolloutTargetGroup.class); - countQueryFrom.join(RolloutTargetGroup_.target); - countQueryFrom.join(RolloutTargetGroup_.actions, JoinType.LEFT); - countQuery.select(cb.count(countQueryFrom)).where(cb - .equal(countQueryFrom.get(RolloutTargetGroup_.rolloutGroup).get(JpaRolloutGroup_.id), rolloutGroupId)); - final Long totalCount = entityManager.createQuery(countQuery).getSingleResult(); - - final CriteriaQuery multiselect = query.multiselect(targetJoin, actionJoin.get(JpaAction_.status)) - .where(cb.equal(targetRoot.get(RolloutTargetGroup_.rolloutGroup).get(JpaRolloutGroup_.id), - rolloutGroupId)) + final CriteriaQuery multiselect = query + .multiselect(targetJoin, actionJoin.get(JpaAction_.status), + actionJoin.get(JpaAction_.lastActionStatusCode)) + .where(getRolloutGroupTargetWithRolloutGroupJoinCondition(rolloutGroupId, cb, targetRoot)) .orderBy(getOrderBy(pageRequest, cb, targetJoin, actionJoin)); final List targetWithActionStatus = entityManager.createQuery(multiselect) .setFirstResult((int) pageRequest.getOffset()).setMaxResults(pageRequest.getPageSize()).getResultList() - .stream().map(o -> new TargetWithActionStatus((Target) o[0], (Action.Status) o[1])) - .collect(Collectors.toList()); + .stream().map(this::getTargetWithActionStatusFromQuery).collect(Collectors.toList()); - return new PageImpl<>(targetWithActionStatus, pageRequest, totalCount); + return new PageImpl<>(targetWithActionStatus, pageRequest, 0); + } + + private Predicate getRolloutGroupTargetWithRolloutGroupJoinCondition(final long rolloutGroupId, final CriteriaBuilder cb, + final Root targetRoot) { + return cb.equal(targetRoot.get(RolloutTargetGroup_.rolloutGroup).get(JpaRolloutGroup_.id), // + rolloutGroupId); + } + + private TargetWithActionStatus getTargetWithActionStatusFromQuery(final Object[] o) { + return new TargetWithActionStatus((Target) o[0], (Action.Status) o[1], + (Integer) o[2]); } private List getOrderBy(final Pageable pageRequest, final CriteriaBuilder cb, @@ -279,8 +283,8 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement { return pageRequest.getSort().get().flatMap(order -> { final List orders; final String property = order.getProperty(); - // we consider status as property from JpaAction ... - if ("status".equals(property)) { + // we consider status, last_action_status_code as property from JpaAction ... + if ("status".equals(property) || "lastActionStatusCode".equals(property)) { orders = QueryUtils.toOrders(Sort.by(order.getDirection(), property), actionJoin, cb); } // ... and every other property from JpaTarget @@ -298,8 +302,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement { final CriteriaBuilder cb = entityManager.getCriteriaBuilder(); final CriteriaQuery countQuery = cb.createQuery(Long.class); final Root countQueryFrom = countQuery.from(RolloutTargetGroup.class); - countQuery.select(cb.count(countQueryFrom)).where(cb - .equal(countQueryFrom.get(RolloutTargetGroup_.rolloutGroup).get(JpaRolloutGroup_.id), rolloutGroupId)); + countQuery.select(cb.count(countQueryFrom)).where(getRolloutGroupTargetWithRolloutGroupJoinCondition(rolloutGroupId, cb, countQueryFrom)); return entityManager.createQuery(countQuery).getSingleResult(); } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java index 2b91edce3..35fa00ad4 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/repository/jpa/model/JpaAction.java @@ -140,6 +140,9 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio @Column(name = "initiated_by", updatable = false, nullable = false, length = USERNAME_FIELD_LENGTH) private String initiatedBy; + @Column(name = "last_action_status_code", nullable = true, updatable = true) + private Integer lastActionStatusCode; + @Override public DistributionSet getDistributionSet() { return distributionSet; @@ -375,4 +378,13 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio public String getInitiatedBy() { return initiatedBy; } + + @Override + public Optional getLastActionStatusCode() { + return Optional.ofNullable(lastActionStatusCode); + } + + public void setLastActionStatusCode(final Integer lastActionStatusCode) { + this.lastActionStatusCode = lastActionStatusCode; + } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/DB2/V1_12_24__add_last_action_status_code___DB2.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/DB2/V1_12_24__add_last_action_status_code___DB2.sql new file mode 100644 index 000000000..ee8e263bd --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/DB2/V1_12_24__add_last_action_status_code___DB2.sql @@ -0,0 +1 @@ +ALTER TABLE sp_action ADD COLUMN last_action_status_code INTEGER; \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_12_24__add_last_action_status_code___H2.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_12_24__add_last_action_status_code___H2.sql new file mode 100644 index 000000000..f92ba4675 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/H2/V1_12_24__add_last_action_status_code___H2.sql @@ -0,0 +1 @@ +ALTER TABLE sp_action ADD column last_action_status_code integer; \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_12_24__add_last_action_status_code___MYSQL.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_12_24__add_last_action_status_code___MYSQL.sql new file mode 100644 index 000000000..03539efb3 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/MYSQL/V1_12_24__add_last_action_status_code___MYSQL.sql @@ -0,0 +1 @@ +ALTER TABLE sp_action ADD COLUMN last_action_status_code integer; \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/POSTGRESQL/V1_12_24__add_last_action_status_code___POSTGRESQL.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/POSTGRESQL/V1_12_24__add_last_action_status_code___POSTGRESQL.sql new file mode 100644 index 000000000..d5a4cc232 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/POSTGRESQL/V1_12_24__add_last_action_status_code___POSTGRESQL.sql @@ -0,0 +1 @@ +ALTER TABLE sp_action ADD last_action_status_code INTEGER; \ No newline at end of file diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_24__add_last_action_status_code___SQL_SERVER.sql b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_24__add_last_action_status_code___SQL_SERVER.sql new file mode 100644 index 000000000..db38eeb10 --- /dev/null +++ b/hawkbit-repository/hawkbit-repository-jpa/src/main/resources/db/migration/SQL_SERVER/V1_12_24__add_last_action_status_code___SQL_SERVER.sql @@ -0,0 +1 @@ +ALTER TABLE sp_action ADD last_action_status_code INT; \ No newline at end of file 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 3ce29d8b9..c25a01760 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 @@ -38,13 +38,14 @@ import org.apache.commons.lang3.RandomUtils; import org.assertj.core.api.Assertions; import org.eclipse.hawkbit.repository.RepositoryProperties; import org.eclipse.hawkbit.repository.UpdateMode; +import org.eclipse.hawkbit.repository.builder.ActionStatusCreate; +import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent; 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.TargetPollEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent; -import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleUpdatedEvent; @@ -421,29 +422,31 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { @Step private void simulateIntermediateStatusOnUpdate(final Long actionId) { - controllerManagement - .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RUNNING)); - assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, Action.Status.RUNNING, - Action.Status.RUNNING, true); + addUpdateActionStatusAndAssert(actionId, Action.Status.RUNNING); - controllerManagement - .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOAD)); - assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, Action.Status.RUNNING, - Action.Status.DOWNLOAD, true); - controllerManagement - .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.DOWNLOADED)); - assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, Action.Status.RUNNING, - Action.Status.DOWNLOADED, true); + addUpdateActionStatusAndAssert(actionId, Action.Status.DOWNLOAD); - controllerManagement - .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.RETRIEVED)); - assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, Action.Status.RUNNING, - Action.Status.RETRIEVED, true); + addUpdateActionStatusAndAssert(actionId, Action.Status.DOWNLOADED); + addUpdateActionStatusAndAssert(actionId, Action.Status.RETRIEVED); + + addUpdateActionStatusAndAssert(actionId, Action.Status.WARNING); + } + + private void addUpdateActionStatusAndAssert(final Long actionId, final Action.Status actionStatus) { + addUpdateActionStatusAndAssert(actionId, actionStatus, null); + } + + private void addUpdateActionStatusAndAssert(final Long actionId, final Action.Status actionStatus, + final Integer code) { + final ActionStatusCreate status = entityFactory.actionStatus().create(actionId).status(actionStatus); + if (code != null) { + status.code(code.intValue()); + } controllerManagement - .addUpdateActionStatus(entityFactory.actionStatus().create(actionId).status(Action.Status.WARNING)); + .addUpdateActionStatus(status); assertActionStatus(actionId, DEFAULT_CONTROLLER_ID, TargetUpdateStatus.PENDING, Action.Status.RUNNING, - Action.Status.WARNING, true); + actionStatus, true); } private void assertActionStatus(final Long actionId, final String controllerId, @@ -1377,7 +1380,7 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { knownControllerId); final Long actionId = getFirstAssignedActionId(assignmentResult); controllerManagement.updateActionExternalRef(actionId, knownExternalRef); - + // WHEN final Optional foundAction = controllerManagement.getActionByExternalRef(knownExternalRef); @@ -1584,4 +1587,26 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest { .as("No EntityNotFoundException thrown when deleting a non-existing target") .isThrownBy(() -> controllerManagement.deleteExistingTarget(target.getControllerId())); } + + @Test + @Description("When action status code is provided in feedback it is also stored in the action field lastActionStatusCode") + void lastActionStatusCodeIsSet() { + final Long actionId = createTargetAndAssignDs(); + + addUpdateActionStatusAndAssert(actionId, Action.Status.RUNNING, 10); + assertLastActionStatusCodeInAction(actionId, 10); + + addUpdateActionStatusAndAssert(actionId, Action.Status.RUNNING); + assertLastActionStatusCodeInAction(actionId, null); + + addUpdateActionStatusAndAssert(actionId, Action.Status.RUNNING, 20); + assertLastActionStatusCodeInAction(actionId, 20); + + } + + private void assertLastActionStatusCodeInAction(final Long actionId, final Integer expectedLastActionStatusCode) { + final Optional action = actionRepository.getById(actionId); + assertThat(action).isPresent(); + assertThat(action.get().getLastActionStatusCode()).isEqualTo(Optional.ofNullable(expectedLastActionStatusCode)); + } } diff --git a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutGroupManagementTest.java b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutGroupManagementTest.java index c5ebe75a2..b50bc7aa7 100644 --- a/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutGroupManagementTest.java +++ b/hawkbit-repository/hawkbit-repository-jpa/src/test/java/org/eclipse/hawkbit/repository/jpa/RolloutGroupManagementTest.java @@ -12,7 +12,6 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.List; -import org.apache.commons.lang3.RandomStringUtils; import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.RolloutCreatedEvent; @@ -22,6 +21,7 @@ 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.TargetCreatedEvent; import org.eclipse.hawkbit.repository.model.Action; +import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.Rollout; import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.Target; @@ -32,6 +32,7 @@ import org.junit.jupiter.api.Test; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; +import org.springframework.util.CollectionUtils; import io.qameta.allure.Description; import io.qameta.allure.Feature; @@ -62,9 +63,8 @@ class RolloutGroupManagementTest extends AbstractJpaIntegrationTest { @Expect(type = RolloutUpdatedEvent.class, count = 1), @Expect(type = TargetCreatedEvent.class, count = 125), @Expect(type = RolloutCreatedEvent.class, count = 1) }) - void entityQueriesReferringToNotExistingEntitiesThrowsException() { - testdataFactory.createRollout("xxx"); + testdataFactory.createRollout(); verifyThrownExceptionBy(() -> rolloutGroupManagement.countByRollout(NOT_EXIST_IDL), "Rollout"); verifyThrownExceptionBy(() -> rolloutGroupManagement.countTargetsOfRolloutsGroup(NOT_EXIST_IDL), @@ -87,14 +87,10 @@ class RolloutGroupManagementTest extends AbstractJpaIntegrationTest { @Test @Description("Verifies that the returned result considers the provided sort parameters.") void findAllTargetsOfRolloutGroupWithActionStatusConsidersSorting() { - final String prefix = RandomStringUtils.randomAlphanumeric(5); - final Rollout rollout = testdataFactory.createRollout(prefix); + final Rollout rollout = testdataFactory.createAndStartRollout(); final List rolloutGroups = rolloutGroupManagement.findByRollout(PAGE, rollout.getId()) .getContent(); final RolloutGroup rolloutGroup = rolloutGroups.get(0); - rolloutManagement.handleRollouts(); - rolloutManagement.start(rollout.getId()); - rolloutManagement.handleRollouts(); rolloutManagement.pauseRollout(rollout.getId()); rolloutManagement.handleRollouts(); final List targets = rolloutGroupManagement.findTargetsOfRolloutGroup(PAGE, rolloutGroup.getId()) @@ -131,6 +127,122 @@ class RolloutGroupManagementTest extends AbstractJpaIntegrationTest { assertThatListIsSortedByTargetName(targetsWithActionStatusOrderedByNameAsc, Direction.ASC); } + @Test + @Description("Verifies that the returned result considers sorting by action status code.") + void findAllTargetsOfRolloutGroupWithActionStatusConsidersSortingByLastActionStatusCode() { + final Rollout rollout = testdataFactory.createAndStartRollout(); + final List rolloutGroups = rolloutGroupManagement.findByRollout(PAGE, rollout.getId()) + .getContent(); + final RolloutGroup rolloutGroup = rolloutGroups.get(0); + final List runningActions = findActionsByRolloutAndStatus(rollout, Status.RUNNING); + final Target target0 = runningActions.get(0).getTarget(); + final Target target24 = CollectionUtils.lastElement(runningActions).getTarget(); + int i = 0; + for (final Action action : runningActions) { + controllerManagement.addUpdateActionStatus( + entityFactory.actionStatus().create(action.getId()).status(Status.RUNNING).code(i++)); + } + + List targetsWithActionStatus = rolloutGroupManagement + .findAllTargetsOfRolloutGroupWithActionStatus( + PageRequest.of(0, 500, Sort.by(Direction.ASC, "lastActionStatusCode")), + rolloutGroup.getId()) + .getContent(); + assertSortedListOfActionStatus(targetsWithActionStatus, target0, 0, target24, 24); + assertThat(targetsWithActionStatus) + .hasSize((int) rolloutGroupManagement.countTargetsOfRolloutsGroup(rolloutGroup.getId())); + + targetsWithActionStatus = rolloutGroupManagement.findAllTargetsOfRolloutGroupWithActionStatus( + PageRequest.of(0, 500, Sort.by(Direction.DESC, "lastActionStatusCode")), rolloutGroup.getId()) + .getContent(); + assertSortedListOfActionStatus(targetsWithActionStatus, target24, 24, target0, 0); + } + + private void assertSortedListOfActionStatus(final List targetsWithActionStatus, + final Target first, final Integer firstStatusCode, final Target last, final Integer lastStatusCode) { + assertTargetAndActionStatusCode(CollectionUtils.firstElement(targetsWithActionStatus), first, firstStatusCode); + assertTargetAndActionStatusCode(CollectionUtils.lastElement(targetsWithActionStatus), last, lastStatusCode); + } + + private void assertTargetAndActionStatusCode(final TargetWithActionStatus targetWithActionStatus, + final Target target, final Integer actionStatusCode) { + assertThat(targetWithActionStatus.getTarget().getControllerId()).isEqualTo(target.getControllerId()); + assertThat(targetWithActionStatus.getLastActionStatusCode()).isEqualTo(actionStatusCode); + } + + private void assertTargetNotNullAndActionStatusNullAndActionStatusCode( + final List targetsWithActionStatus, final Integer actionStatusCode) { + targetsWithActionStatus.forEach(targetWithActionStatus -> { + assertThat(targetWithActionStatus.getTarget().getControllerId()).isNotNull(); + assertThat(targetWithActionStatus.getStatus()).isNull(); + assertThat(targetWithActionStatus.getLastActionStatusCode()).isEqualTo(actionStatusCode); + }); + } + + private void assertTargetNotNullAndActionStatusAndActionStatusCode( + final List targetsWithActionStatus, final Status actionStatus, + final Integer actionStatusCode) { + targetsWithActionStatus.forEach(targetWithActionStatus -> { + assertThat(targetWithActionStatus.getTarget().getControllerId()).isNotNull(); + assertThat(targetWithActionStatus.getStatus()).isEqualTo(actionStatus); + assertThat(targetWithActionStatus.getLastActionStatusCode()).isEqualTo(actionStatusCode); + }); + } + + @Test + @Description("Verifies that Rollouts in different states are handled correctly.") + void findAllTargetsOfRolloutGroupWithActionStatus() { + final Rollout rollout = testdataFactory.createRollout(); + final List rolloutGroups = rolloutGroupManagement.findByRollout(PAGE, rollout.getId()) + .getContent(); + rolloutManagement.handleRollouts(); + + // check query when no actions exist + final List targetsWithActionStatus = rolloutGroupManagement + .findAllTargetsOfRolloutGroupWithActionStatus( + PageRequest.of(0, 500, Sort.by(Direction.DESC, "lastActionStatusCode")), + rolloutGroups.get(0).getId()) + .getContent(); + assertThat(targetsWithActionStatus) + .hasSize((int) rolloutGroupManagement.countTargetsOfRolloutsGroup(rolloutGroups.get(0).getId())); + assertTargetNotNullAndActionStatusNullAndActionStatusCode(targetsWithActionStatus, null); + + rolloutManagement.start(rollout.getId()); + rolloutManagement.handleRollouts(); + + // check query when no action status code exist + final List scheduledActions = findActionsByRolloutAndStatus(rollout, Status.SCHEDULED); + final RolloutGroup rolloutGroupScheduled = scheduledActions.get(0).getRolloutGroup(); + + final List targetsWithActionStatusForScheduledRG = rolloutGroupManagement + .findAllTargetsOfRolloutGroupWithActionStatus( + PageRequest.of(0, 500, Sort.by(Direction.DESC, "lastActionStatusCode")), + rolloutGroupScheduled.getId()) + .getContent(); + assertThat(targetsWithActionStatusForScheduledRG) + .hasSize((int) rolloutGroupManagement.countTargetsOfRolloutsGroup(rolloutGroupScheduled.getId())); + assertTargetNotNullAndActionStatusAndActionStatusCode(targetsWithActionStatusForScheduledRG, + Status.SCHEDULED, null); + + final List runningActions = findActionsByRolloutAndStatus(rollout, Status.RUNNING); + final RolloutGroup rolloutGroupRunning = runningActions.get(0).getRolloutGroup(); + for (final Action action : runningActions) { + controllerManagement.addUpdateActionStatus( + entityFactory.actionStatus().create(action.getId()).status(Status.RUNNING).code(100)); + } + + // check query when action status code exists + final List targetsWithActionStatusForRunningRG = rolloutGroupManagement + .findAllTargetsOfRolloutGroupWithActionStatus( + PageRequest.of(0, 500, Sort.by(Direction.DESC, "lastActionStatusCode")), + rolloutGroupRunning.getId()) + .getContent(); + assertThat(targetsWithActionStatusForRunningRG) + .hasSize((int) rolloutGroupManagement.countTargetsOfRolloutsGroup(rolloutGroupRunning.getId())); + assertTargetNotNullAndActionStatusAndActionStatusCode(targetsWithActionStatusForRunningRG, + Status.RUNNING, 100); + } + private void assertThatListIsSortedByTargetName(final List targets, final Direction sortDirection) { String previousName = null; 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 2bc6cd978..2ddf1fe21 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 @@ -223,7 +223,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 5; final String successCondition = "50"; final String errorCondition = "80"; - final Rollout createdRollout = createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, + final Rollout createdRollout = testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet( + amountTargetsForRollout, amountOtherTargets, amountGroups, successCondition, errorCondition); // verify the split of the target and targetGroup @@ -241,7 +242,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 5; final String successCondition = "50"; final String errorCondition = "80"; - final Rollout createdRollout = createAndStartRollout(amountTargetsForRollout, amountOtherTargets, amountGroups, + final Rollout createdRollout = testdataFactory.createAndStartRollout(amountTargetsForRollout, + amountOtherTargets, amountGroups, successCondition, errorCondition); // verify first group is running @@ -276,7 +278,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 5; final String successCondition = "50"; final String errorCondition = "80"; - final Rollout createdRollout = createAndStartRollout(amountTargetsForRollout, amountOtherTargets, amountGroups, + final Rollout createdRollout = testdataFactory.createAndStartRollout(amountTargetsForRollout, + amountOtherTargets, amountGroups, successCondition, errorCondition); final List runningActions = findActionsByRolloutAndStatus(createdRollout, Status.RUNNING); @@ -319,7 +322,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 3; final String successCondition = "100"; final String errorCondition = "80"; - final Rollout createdRollout = createAndStartRollout(amountTargetsForRollout, amountOtherTargets, amountGroups, + final Rollout createdRollout = testdataFactory.createAndStartRollout(amountTargetsForRollout, + amountOtherTargets, amountGroups, successCondition, errorCondition); finishActionAndDeleteTargetsOfFirstRunningGroup(createdRollout); @@ -334,20 +338,6 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { } - @Step("Create a rollout by the given parameter and start it.") - private Rollout createAndStartRollout(final int amountTargetsForRollout, final int amountOtherTargets, - final int amountGroups, final String successCondition, final String errorCondition) { - - final Rollout createdRollout = createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, - amountOtherTargets, amountGroups, successCondition, errorCondition); - rolloutManagement.start(createdRollout.getId()); - - // Run here, because scheduler is disabled during tests - rolloutManagement.handleRollouts(); - - return reloadRollout(createdRollout); - } - @Step("Finish three actions of the rollout group and delete two targets") private void finishActionAndDeleteTargetsOfFirstRunningGroup(final Rollout createdRollout) { // finish group one by finishing targets and deleting targets @@ -419,7 +409,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 5; final String successCondition = "50"; final String errorCondition = "80"; - final Rollout createdRollout = createAndStartRollout(amountTargetsForRollout, amountOtherTargets, amountGroups, + final Rollout createdRollout = testdataFactory.createAndStartRollout(amountTargetsForRollout, + amountOtherTargets, amountGroups, successCondition, errorCondition); // set both actions in error state so error condition is hit and error @@ -462,7 +453,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 5; final String successCondition = "50"; final String errorCondition = "80"; - final Rollout createdRollout = createAndStartRollout(amountTargetsForRollout, amountOtherTargets, amountGroups, + final Rollout createdRollout = testdataFactory.createAndStartRollout(amountTargetsForRollout, + amountOtherTargets, amountGroups, successCondition, errorCondition); // set both actions in error state so error condition is hit and error @@ -513,7 +505,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 5; final String successCondition = "50"; final String errorCondition = "80"; - final Rollout createdRollout = createAndStartRollout(amountTargetsForRollout, amountOtherTargets, amountGroups, + final Rollout createdRollout = testdataFactory.createAndStartRollout(amountTargetsForRollout, + amountOtherTargets, amountGroups, successCondition, errorCondition); // finish running actions, 2 actions should be finished @@ -551,7 +544,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 4; final String successCondition = "50"; final String errorCondition = "80"; - final Rollout createdRollout = createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, + final Rollout createdRollout = testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet( + amountTargetsForRollout, amountOtherTargets, amountGroups, successCondition, errorCondition); // targets have not started @@ -614,7 +608,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 4; final String successCondition = "50"; final String errorCondition = "80"; - final Rollout createdRollout = createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, + final Rollout createdRollout = testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet( + amountTargetsForRollout, amountOtherTargets, amountGroups, successCondition, errorCondition, ActionType.DOWNLOAD_ONLY, null); // targets have not started @@ -681,7 +676,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 4; final String successCondition = "50"; final String errorCondition = "80"; - final Rollout createdRollout = createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, + final Rollout createdRollout = testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet( + amountTargetsForRollout, amountOtherTargets, amountGroups, successCondition, errorCondition); // 8 targets have not started @@ -718,7 +714,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 4; final String successCondition = "50"; final String errorCondition = "80"; - Rollout createdRollout = createAndStartRollout(amountTargetsForRollout, amountOtherTargets, amountGroups, + Rollout createdRollout = testdataFactory.createAndStartRollout(amountTargetsForRollout, amountOtherTargets, + amountGroups, successCondition, errorCondition); changeStatusForAllRunningActions(createdRollout, Status.FINISHED); @@ -759,7 +756,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 2; final String successCondition = "50"; final String errorCondition = "80"; - Rollout createdRollout = createAndStartRollout(amountTargetsForRollout, amountOtherTargets, amountGroups, + Rollout createdRollout = testdataFactory.createAndStartRollout(amountTargetsForRollout, amountOtherTargets, + amountGroups, successCondition, errorCondition); final DistributionSet ds = createdRollout.getDistributionSet(); @@ -802,7 +800,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 3; final String successCondition = "50"; final String errorCondition = "80"; - Rollout rolloutOne = createAndStartRollout(amountTargetsForRollout, amountOtherTargets, amountGroups, + Rollout rolloutOne = testdataFactory.createAndStartRollout(amountTargetsForRollout, amountOtherTargets, + amountGroups, successCondition, errorCondition); rolloutOne = reloadRollout(rolloutOne); @@ -843,7 +842,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 3; final String successCondition = "50"; final String errorCondition = "80"; - Rollout rolloutOne = createAndStartRollout(amountTargetsForRollout, amountOtherTargets, amountGroups, + Rollout rolloutOne = testdataFactory.createAndStartRollout(amountTargetsForRollout, amountOtherTargets, + amountGroups, successCondition, errorCondition); final DistributionSet distributionSet = rolloutOne.getDistributionSet(); @@ -901,7 +901,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 2; final String successCondition = "50"; final String errorCondition = "80"; - Rollout rolloutOne = createAndStartRollout(amountTargetsForRollout, amountOtherTargets, amountGroups, + Rollout rolloutOne = testdataFactory.createAndStartRollout(amountTargetsForRollout, amountOtherTargets, + amountGroups, successCondition, errorCondition); rolloutOne = reloadRollout(rolloutOne); @@ -926,7 +927,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 2; final String successCondition = "80"; final String errorCondition = "90"; - Rollout rolloutOne = createAndStartRollout(amountTargetsForRollout, amountOtherTargets, amountGroups, + Rollout rolloutOne = testdataFactory.createAndStartRollout(amountTargetsForRollout, amountOtherTargets, + amountGroups, successCondition, errorCondition); rolloutOne = reloadRollout(rolloutOne); @@ -951,7 +953,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 2; final String successCondition = "50"; final String errorCondition = "20"; - Rollout rolloutOne = createAndStartRollout(amountTargetsForRollout, amountOtherTargets, amountGroups, + Rollout rolloutOne = testdataFactory.createAndStartRollout(amountTargetsForRollout, amountOtherTargets, + amountGroups, successCondition, errorCondition); rolloutOne = reloadRollout(rolloutOne); @@ -1591,7 +1594,6 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { assertThatExceptionOfType(AssignmentQuotaExceededException.class) .isThrownBy(() -> rolloutManagement.create(rollout, maxGroups + 1, conditions)) .withMessageContaining("not be greater than " + maxGroups); - } @Test @@ -1621,7 +1623,6 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { assertThatExceptionOfType(RolloutIllegalStateException.class) .isThrownBy(() -> rolloutManagement.start(rolloutId)) .withMessageContaining("can only be started in state ready"); - } @Test @@ -1631,7 +1632,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { approvalStrategy.setApprovalNeeded(false); final String successCondition = "50"; final String errorCondition = "80"; - final Rollout rollout = createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 5, successCondition, + final Rollout rollout = testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 5, + successCondition, errorCondition); assertThat(rollout.getStatus()).isEqualTo(Rollout.RolloutStatus.READY); } @@ -1643,7 +1645,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { approvalStrategy.setApprovalNeeded(true); final String successCondition = "50"; final String errorCondition = "80"; - final Rollout rollout = createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 5, successCondition, + final Rollout rollout = testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 5, + successCondition, errorCondition); assertThat(rollout.getStatus()).isEqualTo(Rollout.RolloutStatus.WAITING_FOR_APPROVAL); } @@ -1654,7 +1657,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { approvalStrategy.setApprovalNeeded(true); final String successCondition = "50"; final String errorCondition = "80"; - final Rollout rollout = createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 5, successCondition, + final Rollout rollout = testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 5, + successCondition, errorCondition); assertThat(rollout.getStatus()).isEqualTo(Rollout.RolloutStatus.WAITING_FOR_APPROVAL); rolloutManagement.approveOrDeny(rollout.getId(), Rollout.ApprovalDecision.APPROVED); @@ -1668,7 +1672,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { approvalStrategy.setApprovalNeeded(true); final String successCondition = "50"; final String errorCondition = "80"; - final Rollout rollout = createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 5, successCondition, + final Rollout rollout = testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 5, + successCondition, errorCondition); assertThat(rollout.getStatus()).isEqualTo(Rollout.RolloutStatus.WAITING_FOR_APPROVAL); rolloutManagement.approveOrDeny(rollout.getId(), Rollout.ApprovalDecision.DENIED); @@ -1691,7 +1696,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 5; final String successCondition = "50"; final String errorCondition = "80"; - final Rollout createdRollout = createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, + final Rollout createdRollout = testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet( + amountTargetsForRollout, amountOtherTargets, amountGroups, successCondition, errorCondition); // test @@ -1723,7 +1729,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountGroups = 5; final String successCondition = "50"; final String errorCondition = "80"; - final Rollout createdRollout = createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, + final Rollout createdRollout = testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet( + amountTargetsForRollout, amountOtherTargets, amountGroups, successCondition, errorCondition); // start the rollout, so it has active running actions and a group which @@ -1809,7 +1816,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { @Description("Creating a rollout without weight value when multi assignment in enabled.") void weightNotRequiredInMultiAssignmentMode() { enableMultiAssignments(); - final Rollout rollout = createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 2, "50", "80", + final Rollout rollout = testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 2, "50", + "80", ActionType.FORCED, null); assertThat(rollout).isNotNull(); } @@ -1818,7 +1826,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { @Description("Creating a rollout with a weight causes an error when multi assignment in disabled.") void weightNotAllowedWhenMultiAssignmentModeNotEnabled() { Assertions.assertThatExceptionOfType(MultiAssignmentIsNotEnabledException.class) - .isThrownBy(() -> createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 2, "50", "80", + .isThrownBy(() -> testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 2, "50", + "80", ActionType.FORCED, 66)); } @@ -1851,7 +1860,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { final int amountOfTargets = 5; final int weight = 99; enableMultiAssignments(); - final Long rolloutId = createSimpleTestRolloutWithTargetsAndDistributionSet(amountOfTargets, 2, amountOfTargets, + final Long rolloutId = testdataFactory + .createSimpleTestRolloutWithTargetsAndDistributionSet(amountOfTargets, 2, amountOfTargets, "80", "50", null, weight).getId(); rolloutManagement.start(rolloutId); rolloutManagement.handleRollouts(); @@ -1865,7 +1875,8 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { @Description("Rollout can be created without weight in single assignment and be started in multi assignment") void createInSingleStartInMultiassigMode() { final int amountOfTargets = 5; - final Long rolloutId = createSimpleTestRolloutWithTargetsAndDistributionSet(amountOfTargets, 2, amountOfTargets, + final Long rolloutId = testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet(amountOfTargets, 2, + amountOfTargets, "80", "50", null, null).getId(); enableMultiAssignments(); @@ -2005,23 +2016,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest { } } - private Rollout createSimpleTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout, - final int amountOtherTargets, final int groupSize, final String successCondition, - final String errorCondition) { - return createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, amountOtherTargets, - groupSize, successCondition, errorCondition, ActionType.FORCED, null); - } - private Rollout createSimpleTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout, - final int amountOtherTargets, final int groupSize, final String successCondition, - final String errorCondition, final ActionType actionType, final Integer weight) { - final DistributionSet rolloutDS = testdataFactory.createDistributionSet("rolloutDS"); - testdataFactory.createTargets(amountTargetsForRollout, "rollout-", "rollout"); - testdataFactory.createTargets(amountOtherTargets, "others-", "rollout"); - final String filterQuery = "controllerId==rollout-*"; - return testdataFactory.createRolloutByVariables("test-rollout-name-1", "test-rollout-description-1", groupSize, - filterQuery, rolloutDS, successCondition, errorCondition, actionType, weight); - } private Rollout createTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout, final int groupSize, final String successCondition, final String errorCondition, final String rolloutName, diff --git a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java index 052ed5536..55f059397 100644 --- a/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java +++ b/hawkbit-repository/hawkbit-repository-test/src/main/java/org/eclipse/hawkbit/repository/test/util/TestdataFactory.java @@ -19,11 +19,13 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.NoSuchElementException; import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.IntStream; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.RandomStringUtils; import org.eclipse.hawkbit.repository.ArtifactManagement; import org.eclipse.hawkbit.repository.Constants; import org.eclipse.hawkbit.repository.ControllerManagement; @@ -44,6 +46,7 @@ import org.eclipse.hawkbit.repository.builder.TagCreate; import org.eclipse.hawkbit.repository.builder.TargetCreate; import org.eclipse.hawkbit.repository.builder.TargetTypeCreate; import org.eclipse.hawkbit.repository.model.Action; +import org.eclipse.hawkbit.repository.model.Action.ActionType; import org.eclipse.hawkbit.repository.model.Action.Status; import org.eclipse.hawkbit.repository.model.ActionStatus; import org.eclipse.hawkbit.repository.model.Artifact; @@ -1200,6 +1203,120 @@ public class TestdataFactory { createDistributionSet(prefix), "50", "5"); } + /** + * Create {@link Rollout} with a new {@link DistributionSet} and + * {@link Target}s. + * + * @return created {@link Rollout} + */ + public Rollout createRollout() { + final String prefix = RandomStringUtils.randomAlphanumeric(5); + createTargets(quotaManagement.getMaxTargetsPerRolloutGroup() * quotaManagement.getMaxRolloutGroupsPerRollout(), + prefix); + return createRolloutByVariables(prefix, prefix + " description", + quotaManagement.getMaxRolloutGroupsPerRollout(), "controllerId==" + prefix + "*", + createDistributionSet(prefix), "50", "5"); + } + + /** + * Create {@link Rollout} with a new {@link DistributionSet} and + * {@link Target}s. + * + * @return created {@link Rollout} + */ + public Rollout createAndStartRollout() { + return startAndReloadRollout(createRollout()); + } + + private Rollout startAndReloadRollout(final Rollout rollout) { + rolloutManagement.start(rollout.getId()); + + // Run here, because scheduler is disabled during tests + rolloutManagement.handleRollouts(); + + return reloadRollout(rollout); + } + + private Rollout reloadRollout(final Rollout rollout) { + return rolloutManagement.get(rollout.getId()).orElseThrow(NoSuchElementException::new); + } + + /** + * Create the data for a simple rollout scenario + * + * @param amountTargetsForRollout + * the amount of targets used for the rollout + * @param amountOtherTargets + * amount of other targets not included in the rollout + * @param groupSize + * the size of the rollout group + * @param successCondition + * success condition + * @param errorCondition + * error condition + * @return the created {@link Rollout} + */ + public Rollout createAndStartRollout(final int amountTargetsForRollout, final int amountOtherTargets, + final int amountGroups, final String successCondition, final String errorCondition) { + + final Rollout createdRollout = createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, + amountOtherTargets, amountGroups, successCondition, errorCondition); + return startAndReloadRollout(createdRollout); + } + + /** + * Create the data for a simple rollout scenario + * + * @param amountTargetsForRollout + * the amount of targets used for the rollout + * @param amountOtherTargets + * amount of other targets not included in the rollout + * @param amountOfGroups + * the size of the rollout group + * @param successCondition + * success condition + * @param errorCondition + * error condition + * @return the created {@link Rollout} + */ + public Rollout createSimpleTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout, + final int amountOtherTargets, final int amountOfGroups, final String successCondition, + final String errorCondition) { + return createSimpleTestRolloutWithTargetsAndDistributionSet(amountTargetsForRollout, amountOtherTargets, + amountOfGroups, successCondition, errorCondition, ActionType.FORCED, null); + } + + /** + * Create the data for a simple rollout scenario + * + * @param amountTargetsForRollout + * the amount of targets used for the rollout + * @param amountOtherTargets + * amount of other targets not included in the rollout + * @param amountOfGroups + * the size of the rollout group + * @param successCondition + * success condition + * @param errorCondition + * error condition + * @param actionType + * action Type + * @param weight + * weight + * @return the created {@link Rollout} + */ + public Rollout createSimpleTestRolloutWithTargetsAndDistributionSet(final int amountTargetsForRollout, + final int amountOtherTargets, final int amountOfGroups, final String successCondition, + final String errorCondition, final ActionType actionType, final Integer weight) { + final String suffix = RandomStringUtils.randomAlphanumeric(5); + final DistributionSet rolloutDS = createDistributionSet("rolloutDS-" + suffix); + createTargets(amountTargetsForRollout, "rollout-" + suffix + "-", "rollout"); + createTargets(amountOtherTargets, "others-" + suffix + "-", "rollout"); + final String filterQuery = "controllerId==rollout-" + suffix + "-*"; + return createRolloutByVariables("rollout-" + suffix, "test-rollout-description", amountOfGroups, + filterQuery, rolloutDS, successCondition, errorCondition, actionType, weight); + } + /** * Create the soft deleted {@link Rollout} with a new {@link DistributionSet} * and {@link Target}s. diff --git a/hawkbit-rest/hawkbit-rest-docs/pom.xml b/hawkbit-rest/hawkbit-rest-docs/pom.xml index 789a3dd01..21949f99c 100644 --- a/hawkbit-rest/hawkbit-rest-docs/pom.xml +++ b/hawkbit-rest/hawkbit-rest-docs/pom.xml @@ -110,7 +110,7 @@ org.asciidoctor asciidoctor-maven-plugin - 1.5.2 + 1.5.8 generate-docs diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/mappers/TargetWithActionStatusToProxyTargetMapper.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/mappers/TargetWithActionStatusToProxyTargetMapper.java index d859bfffe..ddab711ff 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/mappers/TargetWithActionStatusToProxyTargetMapper.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/mappers/TargetWithActionStatusToProxyTargetMapper.java @@ -38,10 +38,8 @@ public class TargetWithActionStatusToProxyTargetMapper proxyTarget.setCreatedDate(SPDateTimeUtil.getFormattedDate(target.getCreatedAt())); proxyTarget.setCreatedBy(UserDetailsFormatter.loadAndFormatCreatedBy(target)); proxyTarget.setLastTargetQuery(target.getLastTargetQuery()); - - if (targetWithActionStatus.getStatus() != null) { - proxyTarget.setStatus(targetWithActionStatus.getStatus()); - } + proxyTarget.setLastActionStatusCode(targetWithActionStatus.getLastActionStatusCode()); + proxyTarget.setStatus(targetWithActionStatus.getStatus()); return proxyTarget; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/proxies/ProxyTarget.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/proxies/ProxyTarget.java index 350c8b6db..59ceed7ad 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/proxies/ProxyTarget.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/common/data/proxies/ProxyTarget.java @@ -36,6 +36,8 @@ public class ProxyTarget extends ProxyNamedEntity implements TypeInfoAware { private Status status; + private Integer lastActionStatusCode; + private String securityToken; private boolean isRequestAttributes; @@ -170,6 +172,25 @@ public class ProxyTarget extends ProxyNamedEntity implements TypeInfoAware { this.status = status; } + /** + * Gets the last action status code as reported by the controller + * + * @return statusCode + */ + public Integer getLastActionStatusCode() { + return lastActionStatusCode; + } + + /** + * Sets the last action status code + * + * @param lastActionStatusCode + * Action status code as reported by the controller + */ + public void setLastActionStatusCode(final Integer lastActionStatusCode) { + this.lastActionStatusCode = lastActionStatusCode; + } + /** * Gets the securityToken * @@ -211,7 +232,7 @@ public class ProxyTarget extends ProxyNamedEntity implements TypeInfoAware { } @Override - public void setTypeInfo(ProxyTypeInfo typeInfo) { + public void setTypeInfo(final ProxyTypeInfo typeInfo) { this.typeInfo = typeInfo; } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rolloutgrouptargets/RolloutGroupTargetGrid.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rolloutgrouptargets/RolloutGroupTargetGrid.java index 18c3f3731..8ebd94136 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rolloutgrouptargets/RolloutGroupTargetGrid.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/rollout/rolloutgrouptargets/RolloutGroupTargetGrid.java @@ -54,7 +54,21 @@ public class RolloutGroupTargetGrid extends AbstractGrid { actionStatusIconSupplier = new RolloutActionStatusIconSupplier<>(i18n, ProxyTarget::getStatus, UIComponentIdProvider.ROLLOUT_GROUP_TARGET_STATUS_LABEL_ID, rolloutGroupManagement, rolloutManagementUIState); + + // the min push size is set to 40 by default. This value is set as page + // size in the page request and is in most cases to + // small and would result in multiple DB calls to fetch more data. + // Because retrieving actions is an expensive operation we want to make + // only one DB call. + // On the other hand the window size could not be retrieved at this + // point in time to calculate how many rows can be displayed so + // set it to a fixed value is a compromise here. + // Value 250 was chosen because with this value in fullscreen on a 4k + // display Vaadin creates one call to data provider. + getDataCommunicator().setMinPushSize(250); + init(); + } private void initFilterMappings() { @@ -80,6 +94,11 @@ public class RolloutGroupTargetGrid extends AbstractGrid { i18n.getMessage("header.status")); GridComponentBuilder.setColumnSortable(statusColumn, "status"); + final Column statusCodeColumn = GridComponentBuilder.addColumn(this, + ProxyTarget::getLastActionStatusCode).setId(SPUILabelDefinitions.VAR_STATUS_CODE) + .setCaption(i18n.getMessage("header.status.code")); + GridComponentBuilder.setColumnSortable(statusCodeColumn, "lastActionStatusCode"); + GridComponentBuilder.addCreatedAndModifiedColumns(this, i18n); getColumns().forEach(column -> column.setHidable(true)); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUILabelDefinitions.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUILabelDefinitions.java index 35a0fad7e..6196e26cf 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUILabelDefinitions.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/utils/SPUILabelDefinitions.java @@ -165,6 +165,10 @@ public final class SPUILabelDefinitions { */ public static final String VAR_STATUS = "status"; + /** + * Status code - column property. + */ + public static final String VAR_STATUS_CODE = "statusCode"; /** * Distribution name and version - column property. */ diff --git a/hawkbit-ui/src/main/resources/messages.properties b/hawkbit-ui/src/main/resources/messages.properties index 0a39b1f6d..6ded9db93 100644 --- a/hawkbit-ui/src/main/resources/messages.properties +++ b/hawkbit-ui/src/main/resources/messages.properties @@ -85,6 +85,7 @@ header.action.copy=Copy header.action.download=Download header.action.delete=Delete header.status=Status +header.status.code=Status Code # event container event.notifcation.target.created = 1 target created