Optimize DB usage on DDI REST API calls (#2264)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-02-07 12:32:51 +02:00
committed by GitHub
parent 2d9073723d
commit 0fc076aaca
7 changed files with 124 additions and 160 deletions

View File

@@ -161,11 +161,10 @@ public class DdiRootController implements DdiRootControllerRestApi {
public ResponseEntity<DdiControllerBase> getControllerBase(final String tenant, final String controllerId) { public ResponseEntity<DdiControllerBase> getControllerBase(final String tenant, final String controllerId) {
log.debug("getControllerBase({})", controllerId); log.debug("getControllerBase({})", controllerId);
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, IpUtil final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(
.getClientIpFromRequest(RequestResponseContextHolder.getHttpServletRequest(), securityProperties)); controllerId, IpUtil.getClientIpFromRequest(RequestResponseContextHolder.getHttpServletRequest(), securityProperties));
final Action activeAction = controllerManagement.findActiveActionWithHighestWeight(controllerId).orElse(null); final Action activeAction = controllerManagement.findActiveActionWithHighestWeight(controllerId).orElse(null);
final Action installedAction = controllerManagement.getInstalledActionByTarget(target).orElse(null);
final Action installedAction = controllerManagement.getInstalledActionByTarget(controllerId).orElse(null);
checkAndCancelExpiredAction(activeAction); checkAndCancelExpiredAction(activeAction);
@@ -173,7 +172,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
return new ResponseEntity<>(DataConversionHelper.fromTarget(target, installedAction, activeAction, return new ResponseEntity<>(DataConversionHelper.fromTarget(target, installedAction, activeAction,
activeAction == null activeAction == null
? controllerManagement.getPollingTime() ? controllerManagement.getPollingTime()
: controllerManagement.getPollingTimeForAction(activeAction.getId()), tenantAware), : controllerManagement.getPollingTimeForAction(activeAction), tenantAware),
HttpStatus.OK); HttpStatus.OK);
} }
@@ -733,7 +732,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
private void checkAndCancelExpiredAction(final Action action) { private void checkAndCancelExpiredAction(final Action action) {
if (action != null && action.hasMaintenanceSchedule() && action.isMaintenanceScheduleLapsed()) { if (action != null && action.hasMaintenanceSchedule() && action.isMaintenanceScheduleLapsed()) {
try { try {
controllerManagement.cancelAction(action.getId()); controllerManagement.cancelAction(action);
} catch (final CancelActionNotAllowedException e) { } catch (final CancelActionNotAllowedException e) {
log.info("Cancel action not allowed: {}", e.getMessage()); log.info("Cancel action not allowed: {}", e.getMessage());
} }

View File

@@ -105,7 +105,7 @@ public interface ControllerManagement {
/** /**
* Retrieves active {@link Action} with the highest priority that is assigned to a {@link Target}. * Retrieves active {@link Action} with the highest priority that is assigned to a {@link Target}.
* * <p/>
* For performance reasons this method does not throw {@link EntityNotFoundException} in case target with given controllerId * For performance reasons this method does not throw {@link EntityNotFoundException} in case target with given controllerId
* does not exist but will return an {@link Optional#empty()} instead. * does not exist but will return an {@link Optional#empty()} instead.
* *
@@ -202,7 +202,7 @@ public interface ControllerManagement {
String getMinPollingTime(); String getMinPollingTime();
/** /**
* Returns the count to be used for reducing polling interval while calling {@link ControllerManagement#getPollingTimeForAction(long)}. * Returns the count to be used for reducing polling interval while calling {@link ControllerManagement#getPollingTimeForAction(Action)}.
* *
* @return configured value of {@link TenantConfigurationKey#MAINTENANCE_WINDOW_POLL_COUNT}. * @return configured value of {@link TenantConfigurationKey#MAINTENANCE_WINDOW_POLL_COUNT}.
*/ */
@@ -215,11 +215,11 @@ public interface ControllerManagement {
* Poll time keeps reducing with MinPollingTime as lower limit {@link TenantConfigurationKey#MIN_POLLING_TIME_INTERVAL}. After the start * Poll time keeps reducing with MinPollingTime as lower limit {@link TenantConfigurationKey#MIN_POLLING_TIME_INTERVAL}. After the start
* of maintenance window, it resets to default {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}. * of maintenance window, it resets to default {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
* *
* @param actionId id the {@link Action} for which polling time is calculated based on it having maintenance window or not * @param action {@link Action} for which polling time is calculated based on it having maintenance window or not
* @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}. * @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
*/ */
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
String getPollingTimeForAction(long actionId); String getPollingTimeForAction(Action action);
/** /**
* Checks if a given target has currently or has even been assigned to the given artifact through the action history list. This can e.g. * Checks if a given target has currently or has even been assigned to the given artifact through the action history list. This can e.g.
@@ -315,16 +315,17 @@ public interface ControllerManagement {
List<String> getActionHistoryMessages(long actionId, int messageCount); List<String> getActionHistoryMessages(long actionId, int messageCount);
/** /**
* Cancels given {@link Action} for this {@link Target}. However, it might be possible that the controller will continue to work on the * Cancels given {@link Action} for this {@link Target}. The method will immediately add a {@link Status#CANCELED}.
* cancellation. The controller needs to acknowledge or reject the cancellation using {@link DdiRootController#postCancelActionFeedback}. * However, it might be possible that the controller will continue to work on the cancellation. The controller needs to acknowledge or
* reject the cancellation using DdiRootController#postCancelActionFeedback
* *
* @param actionId to be canceled * @param action to be canceled
* @return canceled {@link Action} * @return canceled {@link Action}
* @throws CancelActionNotAllowedException in case the given action is not active or is already canceled * @throws CancelActionNotAllowedException in case the given action is not active or is already canceled
* @throws EntityNotFoundException if action with given actionId does not exist. * @throws EntityNotFoundException if action with given actionId does not exist.
*/ */
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Action cancelAction(long actionId); Action cancelAction(Action action);
/** /**
* Updates given {@link Action} with its external id. * Updates given {@link Action} with its external id.
@@ -355,10 +356,10 @@ public interface ControllerManagement {
/** /**
* Finds an {@link Action} based on the target that it's assigned to * Finds an {@link Action} based on the target that it's assigned to
* *
* @param controllerId of the target the action is assigned to * @param target the target the action is assigned to
*/ */
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER) @PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
Optional<Action> getInstalledActionByTarget(@NotEmpty String controllerId); Optional<Action> getInstalledActionByTarget(@NotNull Target target);
/** /**
* Activate auto confirmation for a given controllerId * Activate auto confirmation for a given controllerId

View File

@@ -389,8 +389,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
} }
/** /**
* Returns the count to be used for reducing polling interval while calling * Returns the count to be used for reducing polling interval while calling {@link ControllerManagement#getPollingTimeForAction(Action)}.
* {@link ControllerManagement#getPollingTimeForAction(long)}.
* *
* @return configured value of * @return configured value of
* {@link TenantConfigurationKey#MAINTENANCE_WINDOW_POLL_COUNT}. * {@link TenantConfigurationKey#MAINTENANCE_WINDOW_POLL_COUNT}.
@@ -402,10 +401,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
} }
@Override @Override
public String getPollingTimeForAction(final long actionId) { public String getPollingTimeForAction(final Action action) {
final JpaAction action = getActionAndThrowExceptionIfNotFound(actionId);
if (!action.hasMaintenanceSchedule() || action.isMaintenanceScheduleLapsed()) { if (!action.hasMaintenanceSchedule() || action.isMaintenanceScheduleLapsed()) {
return getPollingTime(); return getPollingTime();
} }
@@ -505,52 +501,35 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
return messages.getContent(); return messages.getContent();
} }
/**
* Cancels given {@link Action} for this {@link Target}. The method will
* immediately add a {@link Status#CANCELED} status to the action. However,
* it might be possible that the controller will continue to work on the
* cancellation. The controller needs to acknowledge or reject the
* cancellation using {@link DdiRootController#postCancelActionFeedback}.
*
* @param actionId to be canceled
* @return canceled {@link Action}
* @throws CancelActionNotAllowedException in case the given action is not active or is already canceled
* @throws EntityNotFoundException if action with given actionId does not exist.
*/
@Override @Override
@Modifying @Modifying
@Transactional(isolation = Isolation.READ_COMMITTED) @Transactional(isolation = Isolation.READ_COMMITTED)
public Action cancelAction(final long actionId) { public Action cancelAction(final Action action) {
log.debug("cancelAction({})", actionId); log.debug("cancelAction({})", action.getId());
final JpaAction action = actionRepository.findById(actionId)
.orElseThrow(() -> new EntityNotFoundException(Action.class, actionId));
if (action.isCancelingOrCanceled()) { if (action.isCancelingOrCanceled()) {
throw new CancelActionNotAllowedException("Actions in canceling or canceled state cannot be canceled"); throw new CancelActionNotAllowedException("Actions in canceling or canceled state cannot be canceled");
} }
if (action.isActive()) { if (action.isActive()) {
log.debug("action ({}) was still active. Change to {}.", action, Status.CANCELING); log.debug("action ({}) was still active. Change to {}.", action, Status.CANCELING);
action.setStatus(Status.CANCELING); final JpaAction jpaAction = (JpaAction)action;
jpaAction.setStatus(Status.CANCELING);
// document that the status has been retrieved // document that the status has been retrieved
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, System.currentTimeMillis(), actionStatusRepository.save(
"manual cancelation requested")); new JpaActionStatus(jpaAction, Status.CANCELING, System.currentTimeMillis(), "manual cancelation requested"));
final Action saveAction = actionRepository.save(action); final Action saveAction = actionRepository.save(jpaAction);
cancelAssignDistributionSetEvent(action);
cancelAssignDistributionSetEvent(jpaAction);
return saveAction; return saveAction;
} else { } else {
throw new CancelActionNotAllowedException( throw new CancelActionNotAllowedException("Action [id: " + action.getId() + "] is not active and cannot be canceled");
"Action [id: " + action.getId() + "] is not active and cannot be canceled");
} }
} }
@Override @Override
public void updateActionExternalRef(final long actionId, @NotEmpty final String externalRef) { public void updateActionExternalRef(final long actionId, @NotEmpty final String externalRef) {
// if access control for target repository is present check that caller has // if access control for target repository is present check that caller has UPDATE access to the target of the action
// UPDATE access to the target of the action
targetRepository.getAccessController().ifPresent( targetRepository.getAccessController().ifPresent(
accessController -> accessController.assertOperationAllowed( accessController -> accessController.assertOperationAllowed(
AccessController.Operation.UPDATE, AccessController.Operation.UPDATE,
@@ -574,20 +553,15 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
} }
@Override @Override
public Optional<Action> getInstalledActionByTarget(final String controllerId) { public Optional<Action> getInstalledActionByTarget(final Target target) {
final JpaDistributionSet installedDistributionSet = targetRepository.getByControllerId(controllerId).getInstalledDistributionSet(); final JpaTarget jpaTarget = (JpaTarget) target;
if (installedDistributionSet != null) { return Optional.ofNullable(jpaTarget.getInstalledDistributionSet())
final JpaTarget jpaTarget = targetRepository.getByControllerId(controllerId); .flatMap(installedDistributionSet -> actionRepository.findFirstByTargetIdAndDistributionSetIdAndStatusOrderByIdDesc(
return actionRepository.findFirstByTargetIdAndDistributionSetIdAndStatusOrderByIdDesc( jpaTarget.getId(), installedDistributionSet.getId(), FINISHED));
jpaTarget.getId(), installedDistributionSet.getId(), FINISHED);
} else {
return Optional.empty();
}
} }
@Override @Override
public AutoConfirmationStatus activateAutoConfirmation(final String controllerId, final String initiator, public AutoConfirmationStatus activateAutoConfirmation(final String controllerId, final String initiator, final String remark) {
final String remark) {
return confirmationManagement.activateAutoConfirmation(controllerId, initiator, remark); return confirmationManagement.activateAutoConfirmation(controllerId, initiator, remark);
} }

View File

@@ -40,6 +40,7 @@ import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min; import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper; import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent; import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
@@ -82,6 +83,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Getter
@ManyToOne(fetch = FetchType.LAZY, optional = false) @ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn( @JoinColumn(
name = "distribution_set", nullable = false, updatable = false, name = "distribution_set", nullable = false, updatable = false,
@@ -89,6 +91,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@NotNull @NotNull
private JpaDistributionSet distributionSet; private JpaDistributionSet distributionSet;
@Getter
@ManyToOne(fetch = FetchType.LAZY, optional = false) @ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn( @JoinColumn(
name = "target", updatable = false, name = "target", updatable = false,
@@ -96,14 +99,20 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@NotNull @NotNull
private JpaTarget target; private JpaTarget target;
@Setter
@Getter
@Column(name = "active") @Column(name = "active")
private boolean active; private boolean active;
@Setter
@Getter
@Column(name = "action_type", nullable = false) @Column(name = "action_type", nullable = false)
@Convert(converter = ActionTypeConverter.class) @Convert(converter = ActionTypeConverter.class)
@NotNull @NotNull
private ActionType actionType; private ActionType actionType;
@Setter
@Getter
@Column(name = "forced_time") @Column(name = "forced_time")
private long forcedTime; private long forcedTime;
@@ -113,6 +122,8 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Max(Action.WEIGHT_MAX) @Max(Action.WEIGHT_MAX)
private Integer weight; private Integer weight;
@Setter
@Getter
@Column(name = "status", nullable = false) @Column(name = "status", nullable = false)
@Convert(converter = StatusConverter.class) @Convert(converter = StatusConverter.class)
@NotNull @NotNull
@@ -121,12 +132,14 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@OneToMany(mappedBy = "action", targetEntity = JpaActionStatus.class, fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE }) @OneToMany(mappedBy = "action", targetEntity = JpaActionStatus.class, fetch = FetchType.LAZY, cascade = { CascadeType.REMOVE })
private List<JpaActionStatus> actionStatus = new ArrayList<>(); private List<JpaActionStatus> actionStatus = new ArrayList<>();
@Getter
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn( @JoinColumn(
name = "rolloutgroup", updatable = false, name = "rolloutgroup", updatable = false,
foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rolloutgroup")) foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rolloutgroup"))
private JpaRolloutGroup rolloutGroup; private JpaRolloutGroup rolloutGroup;
@Getter
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn( @JoinColumn(
name = "rollout", updatable = false, name = "rollout", updatable = false,
@@ -135,24 +148,30 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
// a cron expression to be used for scheduling. // a cron expression to be used for scheduling.
@Setter @Setter
@Getter
@Column(name = "maintenance_cron_schedule", updatable = false, length = Action.MAINTENANCE_WINDOW_SCHEDULE_LENGTH) @Column(name = "maintenance_cron_schedule", updatable = false, length = Action.MAINTENANCE_WINDOW_SCHEDULE_LENGTH)
private String maintenanceWindowSchedule; private String maintenanceWindowSchedule;
// the duration of an available maintenance schedule indexes HH:mm:ss format // the duration of an available maintenance schedule indexes HH:mm:ss format
@Setter @Setter
@Getter
@Column(name = "maintenance_duration", updatable = false, length = Action.MAINTENANCE_WINDOW_DURATION_LENGTH) @Column(name = "maintenance_duration", updatable = false, length = Action.MAINTENANCE_WINDOW_DURATION_LENGTH)
private String maintenanceWindowDuration; private String maintenanceWindowDuration;
// the time zone specified as +/-hh:mm offset from UTC for example +02:00 for CET summer time and +00:00 for UTC. The // the time zone specified as +/-hh:mm offset from UTC for example +02:00 for CET summer time and +00:00 for UTC. The
// start time of a maintenance window calculated based on the cron expression is relative to this time zone. // start time of a maintenance window calculated based on the cron expression is relative to this time zone.
@Setter @Setter
@Getter
@Column(name = "maintenance_time_zone", updatable = false, length = Action.MAINTENANCE_WINDOW_TIMEZONE_LENGTH) @Column(name = "maintenance_time_zone", updatable = false, length = Action.MAINTENANCE_WINDOW_TIMEZONE_LENGTH)
private String maintenanceWindowTimeZone; private String maintenanceWindowTimeZone;
@Setter
@Getter
@Column(name = "external_ref", length = Action.EXTERNAL_REF_MAX_LENGTH) @Column(name = "external_ref", length = Action.EXTERNAL_REF_MAX_LENGTH)
private String externalRef; private String externalRef;
@Setter @Setter
@Getter
@Column(name = "initiated_by", updatable = false, nullable = false, length = USERNAME_FIELD_LENGTH) @Column(name = "initiated_by", updatable = false, nullable = false, length = USERNAME_FIELD_LENGTH)
private String initiatedBy; private String initiatedBy;
@@ -160,113 +179,27 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Column(name = "last_action_status_code", nullable = true, updatable = true) @Column(name = "last_action_status_code", nullable = true, updatable = true)
private Integer lastActionStatusCode; private Integer lastActionStatusCode;
@Override
public DistributionSet getDistributionSet() {
return distributionSet;
}
public void setDistributionSet(final DistributionSet distributionSet) { public void setDistributionSet(final DistributionSet distributionSet) {
this.distributionSet = (JpaDistributionSet) distributionSet; this.distributionSet = (JpaDistributionSet) distributionSet;
} }
@Override
public Status getStatus() {
return status;
}
public void setStatus(final Status status) {
this.status = status;
}
@Override
public boolean isActive() {
return active;
}
public void setActive(final boolean active) {
this.active = active;
}
@Override
public ActionType getActionType() {
return actionType;
}
public void setActionType(final ActionType actionType) {
this.actionType = actionType;
}
@Override
public Target getTarget() {
return target;
}
public void setTarget(final Target target) { public void setTarget(final Target target) {
this.target = (JpaTarget) target; this.target = (JpaTarget) target;
} }
@Override
public long getForcedTime() {
return forcedTime;
}
public void setForcedTime(final long forcedTime) {
this.forcedTime = forcedTime;
}
@Override @Override
public Optional<Integer> getWeight() { public Optional<Integer> getWeight() {
return Optional.ofNullable(weight); return Optional.ofNullable(weight);
} }
@Override
public RolloutGroup getRolloutGroup() {
return rolloutGroup;
}
public void setRolloutGroup(final RolloutGroup rolloutGroup) { public void setRolloutGroup(final RolloutGroup rolloutGroup) {
this.rolloutGroup = (JpaRolloutGroup) rolloutGroup; this.rolloutGroup = (JpaRolloutGroup) rolloutGroup;
} }
@Override
public Rollout getRollout() {
return rollout;
}
public void setRollout(final Rollout rollout) { public void setRollout(final Rollout rollout) {
this.rollout = (JpaRollout) rollout; this.rollout = (JpaRollout) rollout;
} }
@Override
public String getMaintenanceWindowSchedule() {
return maintenanceWindowSchedule;
}
@Override
public String getMaintenanceWindowDuration() {
return maintenanceWindowDuration;
}
@Override
public String getMaintenanceWindowTimeZone() {
return maintenanceWindowTimeZone;
}
@Override
public String getExternalRef() {
return externalRef;
}
@Override
public void setExternalRef(final String externalRef) {
this.externalRef = externalRef;
}
@Override
public String getInitiatedBy() {
return initiatedBy;
}
@Override @Override
public Optional<Integer> getLastActionStatusCode() { public Optional<Integer> getLastActionStatusCode() {
return Optional.ofNullable(lastActionStatusCode); return Optional.ofNullable(lastActionStatusCode);
@@ -285,7 +218,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
@Override @Override
public boolean isMaintenanceScheduleLapsed() { public boolean isMaintenanceScheduleLapsed() {
return !getMaintenanceWindowStartTime().isPresent(); return getMaintenanceWindowStartTime().isEmpty();
} }
@Override @Override

View File

@@ -17,7 +17,12 @@ import io.qameta.allure.Description;
import io.qameta.allure.Feature; import io.qameta.allure.Feature;
import io.qameta.allure.Story; import io.qameta.allure.Story;
import org.eclipse.hawkbit.im.authentication.SpPermission; import org.eclipse.hawkbit.im.authentication.SpPermission;
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.model.Target;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@@ -130,7 +135,16 @@ class ControllerManagementSecurityTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Tests ControllerManagement#getPollingTimeForAction() method") @Description("Tests ControllerManagement#getPollingTimeForAction() method")
void getPollingTimeForActionPermissionsCheck() { void getPollingTimeForActionPermissionsCheck() {
assertPermissions(() -> controllerManagement.getPollingTimeForAction(1L), List.of(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE)); final JpaAction action = new JpaAction();
action.setId(1L);
assertPermissions(() -> {
try {
controllerManagement.getPollingTimeForAction(action);
} catch (final CancelActionNotAllowedException e) {
// expected since action is not found
}
return null;
}, List.of(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE));
} }
@Test @Test
@@ -180,7 +194,16 @@ class ControllerManagementSecurityTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Tests ControllerManagement#cancelAction() method") @Description("Tests ControllerManagement#cancelAction() method")
void cancelActionPermissionsCheck() { void cancelActionPermissionsCheck() {
assertPermissions(() -> controllerManagement.cancelAction(1L), List.of(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE)); final JpaAction action = new JpaAction();
action.setId(1L);
assertPermissions(() -> {
try {
controllerManagement.cancelAction(action);
} catch (final CancelActionNotAllowedException e) {
// expected since action is not found
}
return null;
}, List.of(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE));
} }
@Test @Test
@@ -211,14 +234,17 @@ class ControllerManagementSecurityTest extends AbstractJpaIntegrationTest {
@Test @Test
@Description("Tests ControllerManagement#getInstalledActionByTarget() method") @Description("Tests ControllerManagement#getInstalledActionByTarget() method")
void getInstalledActionByTargetPermissionsCheck() { void getInstalledActionByTargetPermissionsCheck() {
assertPermissions(() -> controllerManagement.getInstalledActionByTarget("controllerId"), final Target target = testdataFactory.createTarget();
assertPermissions(
() -> controllerManagement.getInstalledActionByTarget(target),
List.of(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE)); List.of(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE));
} }
@Test @Test
@Description("Tests ControllerManagement#activateAutoConfirmation() method") @Description("Tests ControllerManagement#activateAutoConfirmation() method")
void activateAutoConfirmationPermissionsCheck() { void activateAutoConfirmationPermissionsCheck() {
assertPermissions(() -> controllerManagement.activateAutoConfirmation("controllerId", "initiator", "remark"), assertPermissions(
() -> controllerManagement.activateAutoConfirmation("controllerId", "initiator", "remark"),
List.of(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE)); List.of(SpPermission.SpringEvalExpressions.CONTROLLER_ROLE));
} }

View File

@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.management;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import java.net.URI;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@@ -19,6 +20,7 @@ import io.qameta.allure.Feature;
import io.qameta.allure.Story; import io.qameta.allure.Story;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest; import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.builder.DynamicRolloutGroupTemplate; import org.eclipse.hawkbit.repository.builder.DynamicRolloutGroupTemplate;
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest; import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction; import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
@@ -27,10 +29,14 @@ import org.eclipse.hawkbit.repository.model.Rollout;
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus; import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
import org.eclipse.hawkbit.repository.model.RolloutGroup; import org.eclipse.hawkbit.repository.model.RolloutGroup;
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus; import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.util.IpUtil;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Direction;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
/** /**
@@ -38,7 +44,13 @@ import org.springframework.test.context.TestPropertySource;
*/ */
@Feature("Component Tests - Repository") @Feature("Component Tests - Repository")
@Story("Rollout Management (Flow)") @Story("Rollout Management (Flow)")
@TestPropertySource(properties = { "hawkbit.server.repository.dynamicRolloutsMinInvolvePeriodMS=-1" }) @TestPropertySource(properties = { "hawkbit.server.repository.dynamicRolloutsMinInvolvePeriodMS=-1",
"logging.level.org.eclipse.persistence=DEBUG",
"spring.jpa.properties.eclipselink.logging.level=FINE",
"spring.jpa.properties.eclipselink.logging.level.sql=FINE",
"spring.jpa.properties.eclipselink.logging.parameters=true",
"logging.level.org.hibernate.SQL=TRACE",
"logging.level.org.hibernate.stat=TRACE"})
class RolloutManagementFlowTest extends AbstractJpaIntegrationTest { class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
@BeforeEach @BeforeEach
@@ -73,6 +85,29 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
assertGroup(groups.get(i), false, i == 0 ? RolloutGroupStatus.RUNNING : RolloutGroupStatus.SCHEDULED, 3); assertGroup(groups.get(i), false, i == 0 ? RolloutGroupStatus.RUNNING : RolloutGroupStatus.SCHEDULED, 3);
} }
System.out.println("--------------------------");
final String controllerId = targetPrefix + 1;
final Target target = controllerManagement.findOrRegisterTargetIfItDoesNotExist(controllerId, URI.create("http://***"));
System.out.println("--------------------------1");
final Action activeAction = controllerManagement.findActiveActionWithHighestWeight(controllerId).orElse(null);
System.out.println("--------------------------2");
final Action installedAction = controllerManagement.getInstalledActionByTarget(target).orElse(null);
System.out.println("--------------------------3");
if (activeAction != null && activeAction.hasMaintenanceSchedule() && activeAction.isMaintenanceScheduleLapsed()) {
try {
controllerManagement.cancelAction(activeAction);
System.out.println("--------------------------4");
} catch (final CancelActionNotAllowedException e) {
e.printStackTrace();
}
}
var t = activeAction == null
? controllerManagement.getPollingTime()
: controllerManagement.getPollingTimeForAction(activeAction);
System.out.println("--------------------------");
executeStaticWithoutOneTargetFromTheLastGroupAndHandleAll(groups, rollout, amountGroups); executeStaticWithoutOneTargetFromTheLastGroupAndHandleAll(groups, rollout, amountGroups);
rolloutManagement.pauseRollout(rollout.getId()); rolloutManagement.pauseRollout(rollout.getId());

View File

@@ -53,11 +53,9 @@ public final class IpUtil {
* @return the {@link URI} based IP address from the client which sent the * @return the {@link URI} based IP address from the client which sent the
* request * request
*/ */
public static URI getClientIpFromRequest(final HttpServletRequest request, public static URI getClientIpFromRequest(final HttpServletRequest request, final HawkbitSecurityProperties securityProperties) {
final HawkbitSecurityProperties securityProperties) { return getClientIpFromRequest(
request, securityProperties.getClients().getRemoteIpHeader(), securityProperties.getClients().isTrackRemoteIp());
return getClientIpFromRequest(request, securityProperties.getClients().getRemoteIpHeader(),
securityProperties.getClients().isTrackRemoteIp());
} }
/** /**
@@ -145,10 +143,8 @@ public final class IpUtil {
return uri != null && !(AMQP_SCHEME.equals(uri.getScheme()) || HIDDEN_IP.equals(uri.getHost())); return uri != null && !(AMQP_SCHEME.equals(uri.getScheme()) || HIDDEN_IP.equals(uri.getHost()));
} }
private static URI getClientIpFromRequest(final HttpServletRequest request, final String forwardHeader, private static URI getClientIpFromRequest(final HttpServletRequest request, final String forwardHeader, final boolean trackRemoteIp) {
final boolean trackRemoteIp) {
String ip; String ip;
if (trackRemoteIp) { if (trackRemoteIp) {
ip = request.getHeader(forwardHeader); ip = request.getHeader(forwardHeader);
if (ip == null || (ip = findClientIpAddress(ip)) == null) { if (ip == null || (ip = findClientIpAddress(ip)) == null) {