Fix sonar issues and add DMF tests for maintenance window feature (#655)
* Fix sonar issues. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * POM cleanup and more sonar issues fixed. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Remove unneeded JavaDocs. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * More sonar issues. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * More issues. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Adapt maintenance window to naming. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Add DMF tests. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Readibility. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Typos fixed. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
@@ -50,7 +50,6 @@
|
||||
<dependency>
|
||||
<groupId>com.cronutils</groupId>
|
||||
<artifactId>cron-utils</artifactId>
|
||||
<version>5.0.5</version>
|
||||
</dependency>
|
||||
|
||||
<!-- TEST -->
|
||||
|
||||
@@ -236,7 +236,7 @@ public interface ControllerManagement {
|
||||
* {@link TenantConfigurationKey#MAINTENANCE_WINDOW_POLL_COUNT}.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
public int getMaintenanceWindowPollCount();
|
||||
int getMaintenanceWindowPollCount();
|
||||
|
||||
/**
|
||||
* Returns polling time based on the maintenance window for an action.
|
||||
@@ -248,14 +248,14 @@ public interface ControllerManagement {
|
||||
* of maintenance window, it resets to default
|
||||
* {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
|
||||
*
|
||||
* @param action
|
||||
* @param actionId
|
||||
* id the {@link Action} for which polling time is calculated
|
||||
* based on it having maintenance window or not
|
||||
*
|
||||
* @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.IS_CONTROLLER)
|
||||
String getPollingTimeForAction(final Action action);
|
||||
String getPollingTimeForAction(long actionId);
|
||||
|
||||
/**
|
||||
* Checks if a given target has currently or has even been assigned to the
|
||||
|
||||
@@ -16,10 +16,9 @@ import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.Optional;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidMaintenanceScheduleException;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.cronutils.model.Cron;
|
||||
import com.cronutils.model.definition.CronDefinition;
|
||||
@@ -35,9 +34,7 @@ import com.cronutils.parser.CronParser;
|
||||
*/
|
||||
public class MaintenanceScheduleHelper {
|
||||
|
||||
ExecutionTime scheduleExecutor = null;
|
||||
Duration duration = null;
|
||||
TimeZone timeZone = null;
|
||||
private final ExecutionTime scheduleExecutor;
|
||||
|
||||
/**
|
||||
* Constructor that accepts a cron expression, duration and time zone and
|
||||
@@ -48,22 +45,11 @@ public class MaintenanceScheduleHelper {
|
||||
* maintenance window. Expression has 6 mandatory fields and 1
|
||||
* last optional field: "second minute hour dayofmonth month
|
||||
* weekday year"
|
||||
* @param duration
|
||||
* in HH:mm:ss format specifying the duration of a maintenance
|
||||
* window, for example 00:30:00 for 30 minutes
|
||||
* @param timezone
|
||||
* is 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.
|
||||
*/
|
||||
public MaintenanceScheduleHelper(String cronSchedule, String duration, String timeZone) {
|
||||
this.timeZone = TimeZone.getTimeZone(ZoneOffset.of(timeZone));
|
||||
this.duration = Duration.parse(convertToISODuration(duration));
|
||||
|
||||
CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(QUARTZ);
|
||||
CronParser parser = new CronParser(cronDefinition);
|
||||
Cron quartzCron = parser.parse(cronSchedule);
|
||||
public MaintenanceScheduleHelper(final String cronSchedule) {
|
||||
final CronDefinition cronDefinition = CronDefinitionBuilder.instanceDefinitionFor(QUARTZ);
|
||||
final CronParser parser = new CronParser(cronDefinition);
|
||||
final Cron quartzCron = parser.parse(cronSchedule);
|
||||
this.scheduleExecutor = ExecutionTime.forCron(quartzCron);
|
||||
}
|
||||
|
||||
@@ -78,11 +64,13 @@ public class MaintenanceScheduleHelper {
|
||||
* @return {@link Optional<ZonedDateTime>} of the next available window. In
|
||||
* case there is none, returns empty value.
|
||||
*/
|
||||
public Optional<ZonedDateTime> nextExecution(ZonedDateTime after) {
|
||||
// Exception squid:S1166 - lib throws exception as well if no value found
|
||||
@SuppressWarnings("squid:S1166")
|
||||
public Optional<ZonedDateTime> nextExecution(final ZonedDateTime after) {
|
||||
try {
|
||||
ZonedDateTime next = this.scheduleExecutor.nextExecution(after);
|
||||
return Optional.of(next);
|
||||
} catch (IllegalArgumentException e) {
|
||||
final ZonedDateTime next = this.scheduleExecutor.nextExecution(after);
|
||||
return Optional.ofNullable(next);
|
||||
} catch (final IllegalArgumentException ignored) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -98,7 +86,7 @@ public class MaintenanceScheduleHelper {
|
||||
* @return true if there is at least one valid schedule remaining, else
|
||||
* false.
|
||||
*/
|
||||
public boolean hasValidScheduleAfter(ZonedDateTime after) {
|
||||
private boolean hasValidScheduleAfter(final ZonedDateTime after) {
|
||||
return nextExecution(after).isPresent();
|
||||
}
|
||||
|
||||
@@ -122,34 +110,41 @@ public class MaintenanceScheduleHelper {
|
||||
* start time of a maintenance window calculated based on the
|
||||
* cron expression is relative to this time zone
|
||||
*
|
||||
* @return true if the schedule is valid, else throw an exception
|
||||
*
|
||||
* @throws InvalidMaintenanceScheduleException
|
||||
* if the defined schedule fails the validity criteria.
|
||||
*/
|
||||
public static boolean validateMaintenanceSchedule(String cronSchedule, String duration, String timezone) {
|
||||
// check if schedule, duration and timezone are all not null.
|
||||
if (cronSchedule != null && duration != null && timezone != null) {
|
||||
// check if schedule, duration and timezone are all not empty.
|
||||
if (!(cronSchedule.isEmpty() || duration.isEmpty() || timezone.isEmpty())) {
|
||||
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.of(timezone));
|
||||
MaintenanceScheduleHelper scheduleHelper = new MaintenanceScheduleHelper(cronSchedule, duration,
|
||||
timezone);
|
||||
// check if there is a window currently active or available in
|
||||
// future.
|
||||
if (!scheduleHelper.hasValidScheduleAfter(now.minus(Duration.parse(convertToISODuration(duration))))) {
|
||||
throw new InvalidMaintenanceScheduleException(
|
||||
"No valid maintenance window available after current time");
|
||||
}
|
||||
} else {
|
||||
throw new InvalidMaintenanceScheduleException("Either of schedule, duration or timezone empty.");
|
||||
public static void validateMaintenanceSchedule(final String cronSchedule, final String duration,
|
||||
final String timezone) {
|
||||
// check if schedule, duration and timezone are all not empty.
|
||||
if (allNotEmpty(cronSchedule, duration, timezone)) {
|
||||
final ZonedDateTime now;
|
||||
try {
|
||||
now = ZonedDateTime.now(ZoneOffset.of(timezone));
|
||||
Duration.parse(convertToISODuration(duration));
|
||||
} catch (final RuntimeException validationFailed) {
|
||||
throw new InvalidMaintenanceScheduleException("No valid maintenance window provided", validationFailed);
|
||||
}
|
||||
} else if (!(cronSchedule == null && duration == null && timezone == null)) {
|
||||
|
||||
final MaintenanceScheduleHelper scheduleHelper = new MaintenanceScheduleHelper(cronSchedule);
|
||||
// check if there is a window currently active or available in
|
||||
// future.
|
||||
if (!scheduleHelper.hasValidScheduleAfter(now.minus(Duration.parse(convertToISODuration(duration))))) {
|
||||
throw new InvalidMaintenanceScheduleException(
|
||||
"No valid maintenance window available after current time");
|
||||
}
|
||||
|
||||
} else if (atLeastOneNotEmpty(cronSchedule, duration, timezone)) {
|
||||
throw new InvalidMaintenanceScheduleException(
|
||||
"All of schedule, duration and timezone should either be null or non empty.");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
private static boolean atLeastOneNotEmpty(final String cronSchedule, final String duration, final String timezone) {
|
||||
return !(StringUtils.isEmpty(cronSchedule) && StringUtils.isEmpty(duration) && StringUtils.isEmpty(timezone));
|
||||
}
|
||||
|
||||
private static boolean allNotEmpty(final String cronSchedule, final String duration, final String timezone) {
|
||||
return !StringUtils.isEmpty(cronSchedule) && !StringUtils.isEmpty(duration) && !StringUtils.isEmpty(timezone);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,7 +161,7 @@ public class MaintenanceScheduleHelper {
|
||||
* @throws DateTimeParseException
|
||||
* if the text cannot be converted to ISO format.
|
||||
*/
|
||||
public static String convertToISODuration(String timeInterval) {
|
||||
public static String convertToISODuration(final String timeInterval) {
|
||||
return Duration.between(LocalTime.MIN, LocalTime.parse(timeInterval)).toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent {
|
||||
|
||||
private long distributionSetId;
|
||||
|
||||
private boolean maintenanceWindowAvailable;
|
||||
|
||||
private final Map<String, Long> actions = new HashMap<>();
|
||||
|
||||
/**
|
||||
@@ -46,24 +48,32 @@ public class TargetAssignDistributionSetEvent extends RemoteTenantAwareEvent {
|
||||
* the actions and the targets
|
||||
* @param applicationId
|
||||
* the application id.
|
||||
* @param maintenanceWindowAvailable
|
||||
* see {@link Action#isMaintenanceWindowAvailable()}
|
||||
*/
|
||||
public TargetAssignDistributionSetEvent(final String tenant, final long distributionSetId, final List<Action> a,
|
||||
final String applicationId) {
|
||||
final String applicationId, final boolean maintenanceWindowAvailable) {
|
||||
super(distributionSetId, tenant, applicationId);
|
||||
this.distributionSetId = distributionSetId;
|
||||
this.maintenanceWindowAvailable = maintenanceWindowAvailable;
|
||||
actions.putAll(a.stream().filter(action -> action.getDistributionSet().getId().longValue() == distributionSetId)
|
||||
.collect(Collectors.toMap(action -> action.getTarget().getControllerId(), Action::getId)));
|
||||
|
||||
}
|
||||
|
||||
public TargetAssignDistributionSetEvent(final Action action, final String applicationId) {
|
||||
this(action.getTenant(), action.getDistributionSet().getId(), Arrays.asList(action), applicationId);
|
||||
this(action.getTenant(), action.getDistributionSet().getId(), Arrays.asList(action), applicationId,
|
||||
action.isMaintenanceWindowAvailable());
|
||||
}
|
||||
|
||||
public Long getDistributionSetId() {
|
||||
return distributionSetId;
|
||||
}
|
||||
|
||||
public boolean isMaintenanceWindowAvailable() {
|
||||
return maintenanceWindowAvailable;
|
||||
}
|
||||
|
||||
public Map<String, Long> getActions() {
|
||||
return actions;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
|
||||
/**
|
||||
* A custom view on {@link Target} with {@link ActionType}.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class TargetWithActionType {
|
||||
@@ -23,9 +21,9 @@ public class TargetWithActionType {
|
||||
private final String controllerId;
|
||||
private final ActionType actionType;
|
||||
private final long forceTime;
|
||||
private String maintenanceSchedule = null;
|
||||
private String maintenanceWindowDuration = null;
|
||||
private String maintenanceWindowTimeZone = null;
|
||||
private String maintenanceSchedule;
|
||||
private String maintenanceWindowDuration;
|
||||
private String maintenanceWindowTimeZone;
|
||||
|
||||
public TargetWithActionType(final String controllerId) {
|
||||
this.controllerId = controllerId;
|
||||
@@ -35,7 +33,7 @@ public class TargetWithActionType {
|
||||
|
||||
public TargetWithActionType(final String controllerId, final ActionType actionType, final long forceTime) {
|
||||
this.controllerId = controllerId;
|
||||
this.actionType = actionType;
|
||||
this.actionType = actionType != null ? actionType : ActionType.FORCED;
|
||||
this.forceTime = forceTime;
|
||||
}
|
||||
|
||||
@@ -64,19 +62,16 @@ public class TargetWithActionType {
|
||||
* if the parameters do not define a valid maintenance schedule.
|
||||
*/
|
||||
public TargetWithActionType(final String controllerId, final ActionType actionType, final long forceTime,
|
||||
String maintenanceSchedule, String maintenanceWindowDuration, String maintenanceWindowTimeZone) {
|
||||
this.controllerId = controllerId;
|
||||
this.actionType = actionType;
|
||||
this.forceTime = forceTime;
|
||||
final String maintenanceSchedule, final String maintenanceWindowDuration,
|
||||
final String maintenanceWindowTimeZone) {
|
||||
this(controllerId, actionType, forceTime);
|
||||
|
||||
if (MaintenanceScheduleHelper.validateMaintenanceSchedule(maintenanceSchedule, maintenanceWindowDuration,
|
||||
maintenanceWindowTimeZone)) {
|
||||
this.maintenanceSchedule = maintenanceSchedule;
|
||||
this.maintenanceWindowDuration = maintenanceWindowDuration;
|
||||
this.maintenanceWindowTimeZone = maintenanceWindowTimeZone;
|
||||
} else {
|
||||
throw new InvalidMaintenanceScheduleException("Invalid maintenance window definition");
|
||||
}
|
||||
this.maintenanceSchedule = maintenanceSchedule;
|
||||
this.maintenanceWindowDuration = maintenanceWindowDuration;
|
||||
this.maintenanceWindowTimeZone = maintenanceWindowTimeZone;
|
||||
|
||||
MaintenanceScheduleHelper.validateMaintenanceSchedule(maintenanceSchedule, maintenanceWindowDuration,
|
||||
maintenanceWindowTimeZone);
|
||||
}
|
||||
|
||||
public ActionType getActionType() {
|
||||
|
||||
@@ -129,8 +129,9 @@ public abstract class AbstractDsAssignmentStrategy {
|
||||
return;
|
||||
}
|
||||
|
||||
afterCommit.afterCommit(() -> eventPublisher.publishEvent(
|
||||
new TargetAssignDistributionSetEvent(tenant, distributionSetId, actions, applicationContext.getId())));
|
||||
afterCommit.afterCommit(
|
||||
() -> eventPublisher.publishEvent(new TargetAssignDistributionSetEvent(tenant, distributionSetId,
|
||||
actions, applicationContext.getId(), actions.get(0).isMaintenanceWindowAvailable())));
|
||||
}
|
||||
|
||||
protected void sendTargetUpdatedEvent(final JpaTarget target) {
|
||||
|
||||
@@ -14,10 +14,6 @@ import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalUnit;
|
||||
import java.util.Collection;
|
||||
import java.time.Duration;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.time.temporal.TemporalUnit;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -62,6 +58,7 @@ import org.eclipse.hawkbit.repository.jpa.specifications.ActionSpecifications;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
@@ -210,31 +207,17 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
.getConfigurationValue(TenantConfigurationKey.MAINTENANCE_WINDOW_POLL_COUNT, Integer.class).getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns polling time based on the maintenance window for an action.
|
||||
* Server will reduce the polling interval as the start time for maintenance
|
||||
* window approaches, so that at least these many attempts are made between
|
||||
* current polling until start of maintenance window. 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}.
|
||||
*
|
||||
* @param action
|
||||
* id the {@link Action} for which polling time is calculated
|
||||
* based on it having maintenance window or not
|
||||
*
|
||||
* @return current {@link TenantConfigurationKey#POLLING_TIME_INTERVAL}.
|
||||
*/
|
||||
@Override
|
||||
public String getPollingTimeForAction(final Action action) {
|
||||
if (action == null || !action.hasMaintenanceSchedule() || action.isMaintenanceScheduleLapsed()) {
|
||||
public String getPollingTimeForAction(final long actionId) {
|
||||
|
||||
final JpaAction action = getActionAndThrowExceptionIfNotFound(actionId);
|
||||
|
||||
if (!action.hasMaintenanceSchedule() || action.isMaintenanceScheduleLapsed()) {
|
||||
return getPollingTime();
|
||||
}
|
||||
|
||||
JpaAction jpaAction = getActionAndThrowExceptionIfNotFound(action.getId());
|
||||
return (new EventTimer(getPollingTime(), getMinPollingTime(), ChronoUnit.SECONDS))
|
||||
.timeToNextEvent(getMaintenanceWindowPollCount(), jpaAction.getMaintenanceWindowStartTime().get());
|
||||
.timeToNextEvent(getMaintenanceWindowPollCount(), action.getMaintenanceWindowStartTime().orElse(null));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -244,7 +227,7 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
* polling, should happen when timer expires. Class makes use of java.time
|
||||
* package to manipulate and calculate timer duration.
|
||||
*/
|
||||
private class EventTimer {
|
||||
private static class EventTimer {
|
||||
|
||||
private final String defaultEventInterval;
|
||||
private final Duration defaultEventIntervalDuration;
|
||||
@@ -266,7 +249,7 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
* @param timerUnit
|
||||
* representing the unit of time to be used for timer.
|
||||
*/
|
||||
EventTimer(String defaultEventInterval, String minimumEventInterval, TemporalUnit timeUnit) {
|
||||
EventTimer(final String defaultEventInterval, final String minimumEventInterval, final TemporalUnit timeUnit) {
|
||||
this.defaultEventInterval = defaultEventInterval;
|
||||
this.defaultEventIntervalDuration = Duration
|
||||
.parse(MaintenanceScheduleHelper.convertToISODuration(defaultEventInterval));
|
||||
@@ -294,8 +277,8 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
*
|
||||
* @return String in HH:mm:ss format for time to next event.
|
||||
*/
|
||||
String timeToNextEvent(int eventCount, ZonedDateTime timerResetTime) {
|
||||
ZonedDateTime currentTime = ZonedDateTime.now();
|
||||
String timeToNextEvent(final int eventCount, final ZonedDateTime timerResetTime) {
|
||||
final ZonedDateTime currentTime = ZonedDateTime.now();
|
||||
|
||||
// If there is no reset time, or if we already past the reset time,
|
||||
// return the default interval.
|
||||
@@ -304,7 +287,7 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
}
|
||||
|
||||
// Calculate the interval timer based on desired event count.
|
||||
Duration currentIntervalDuration = Duration.of(currentTime.until(timerResetTime, timeUnit), timeUnit)
|
||||
final Duration currentIntervalDuration = Duration.of(currentTime.until(timerResetTime, timeUnit), timeUnit)
|
||||
.dividedBy(eventCount);
|
||||
|
||||
// Need not return interval greater than the default.
|
||||
@@ -810,7 +793,8 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
// For negative and large value of messageCount, limit the number of
|
||||
// messages.
|
||||
final int limit = messageCount < 0 || messageCount >= RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT
|
||||
? RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT : messageCount;
|
||||
? RepositoryConstants.MAX_ACTION_HISTORY_MSG_COUNT
|
||||
: messageCount;
|
||||
|
||||
final PageRequest pageable = new PageRequest(0, limit, new Sort(Direction.DESC, "occurredAt"));
|
||||
final Page<String> messages = actionStatusRepository.findMessagesByActionIdAndMessageNotLike(pageable, actionId,
|
||||
@@ -913,9 +897,10 @@ public class JpaControllerManagement implements ControllerManagement {
|
||||
* @throws EntityNotFoundException
|
||||
* if action with given actionId does not exist.
|
||||
*/
|
||||
@Override
|
||||
@Modifying
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
public Action cancelAction(long actionId) {
|
||||
public Action cancelAction(final long actionId) {
|
||||
LOG.debug("cancelAction({})", actionId);
|
||||
|
||||
final JpaAction action = actionRepository.findById(actionId)
|
||||
|
||||
@@ -402,6 +402,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
}
|
||||
|
||||
final String tenant = rolloutGroupActions.getContent().get(0).getTenant();
|
||||
final boolean maintenanceWindowAvailable = rolloutGroupActions.getContent().get(0)
|
||||
.isMaintenanceWindowAvailable();
|
||||
|
||||
final List<Action> targetAssignments = rolloutGroupActions.getContent().stream()
|
||||
.map(action -> (JpaAction) action).map(this::closeActionIfSetWasAlreadyAssigned)
|
||||
@@ -410,7 +412,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
|
||||
if (!CollectionUtils.isEmpty(targetAssignments)) {
|
||||
afterCommit.afterCommit(() -> eventPublisher.publishEvent(new TargetAssignDistributionSetEvent(tenant,
|
||||
distributionSetId, targetAssignments, applicationContext.getId())));
|
||||
distributionSetId, targetAssignments, applicationContext.getId(), maintenanceWindowAvailable)));
|
||||
}
|
||||
|
||||
return rolloutGroupActions.getTotalElements();
|
||||
|
||||
@@ -101,7 +101,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
@ConversionValue(objectValue = "DOWNLOAD", dataValue = "7"),
|
||||
@ConversionValue(objectValue = "SCHEDULED", dataValue = "8"),
|
||||
@ConversionValue(objectValue = "CANCEL_REJECTED", dataValue = "9"),
|
||||
@ConversionValue(objectValue = "DOWNLOADED", dataValue = "10")})
|
||||
@ConversionValue(objectValue = "DOWNLOADED", dataValue = "10") })
|
||||
@Convert("status")
|
||||
@NotNull
|
||||
private Status status;
|
||||
@@ -118,13 +118,13 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
@JoinColumn(name = "rollout", updatable = false, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_action_rollout"))
|
||||
private JpaRollout rollout;
|
||||
|
||||
@Column(name = "maintenance_cron_schedule", length = Action.MAINTENANCE_SCHEDULE_CRON_LENGTH)
|
||||
@Column(name = "maintenance_cron_schedule", updatable = false, length = Action.MAINTENANCE_SCHEDULE_CRON_LENGTH)
|
||||
private String maintenanceSchedule;
|
||||
|
||||
@Column(name = "maintenance_duration", length = Action.MAINTENANCE_WINDOW_DURATION_LENGTH)
|
||||
@Column(name = "maintenance_duration", updatable = false, length = Action.MAINTENANCE_WINDOW_DURATION_LENGTH)
|
||||
private String maintenanceWindowDuration;
|
||||
|
||||
@Column(name = "maintenance_time_zone", length = Action.MAINTENANCE_WINDOW_TIMEZONE_LENGTH)
|
||||
@Column(name = "maintenance_time_zone", updatable = false, length = Action.MAINTENANCE_WINDOW_TIMEZONE_LENGTH)
|
||||
private String maintenanceWindowTimeZone;
|
||||
|
||||
/**
|
||||
@@ -243,7 +243,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
* @param maintenanceSchedule
|
||||
* is a cron expression to be used for scheduling.
|
||||
*/
|
||||
public void setMaintenanceSchedule(String maintenanceSchedule) {
|
||||
public void setMaintenanceSchedule(final String maintenanceSchedule) {
|
||||
this.maintenanceSchedule = maintenanceSchedule;
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
* is the duration of an available maintenance schedule in
|
||||
* HH:mm:ss format.
|
||||
*/
|
||||
public void setMaintenanceWindowDuration(String maintenanceWindowDuration) {
|
||||
public void setMaintenanceWindowDuration(final String maintenanceWindowDuration) {
|
||||
this.maintenanceWindowDuration = maintenanceWindowDuration;
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
* start time of a maintenance window calculated based on the
|
||||
* cron expression is relative to this time zone.
|
||||
*/
|
||||
public void setMaintenanceWindowTimeZone(String maintenanceWindowTimeZone) {
|
||||
public void setMaintenanceWindowTimeZone(final String maintenanceWindowTimeZone) {
|
||||
this.maintenanceWindowTimeZone = maintenanceWindowTimeZone;
|
||||
}
|
||||
|
||||
@@ -277,10 +277,9 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
*
|
||||
* @return the {@link MaintenanceScheduleHelper} object.
|
||||
*/
|
||||
MaintenanceScheduleHelper getScheduler() {
|
||||
private MaintenanceScheduleHelper getScheduler() {
|
||||
if (this.scheduleHelper == null) {
|
||||
this.scheduleHelper = new MaintenanceScheduleHelper(maintenanceSchedule, maintenanceWindowDuration,
|
||||
maintenanceWindowTimeZone);
|
||||
this.scheduleHelper = new MaintenanceScheduleHelper(maintenanceSchedule);
|
||||
}
|
||||
return this.scheduleHelper;
|
||||
}
|
||||
@@ -290,7 +289,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
*
|
||||
* @return the {@link Duration} of each maintenance window.
|
||||
*/
|
||||
Duration getMaintenanceWindowDuration() {
|
||||
private Duration getMaintenanceWindowDuration() {
|
||||
return Duration.parse(MaintenanceScheduleHelper.convertToISODuration(this.maintenanceWindowDuration));
|
||||
}
|
||||
|
||||
@@ -302,7 +301,7 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
* @return the start time as {@link Optional<ZonedDateTime>}.
|
||||
*/
|
||||
public Optional<ZonedDateTime> getMaintenanceWindowStartTime() {
|
||||
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.of(maintenanceWindowTimeZone));
|
||||
final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.of(maintenanceWindowTimeZone));
|
||||
return getScheduler().nextExecution(now.minus(getMaintenanceWindowDuration()));
|
||||
}
|
||||
|
||||
@@ -313,50 +312,21 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
*
|
||||
* @return the end time of window as {@link Optional<ZonedDateTime>}.
|
||||
*/
|
||||
public Optional<ZonedDateTime> getMaintenanceWindowEndTime() {
|
||||
if (getMaintenanceWindowStartTime().isPresent()) {
|
||||
return Optional.of(getMaintenanceWindowStartTime().get().plus(getMaintenanceWindowDuration()));
|
||||
}
|
||||
return Optional.empty();
|
||||
private Optional<ZonedDateTime> getMaintenanceWindowEndTime() {
|
||||
return getMaintenanceWindowStartTime().map(start -> start.plus(getMaintenanceWindowDuration()));
|
||||
}
|
||||
|
||||
/**
|
||||
* The method checks whether the action has a maintenance schedule defined
|
||||
* for it. A maintenance schedule defines a set of maintenance windows
|
||||
* during which actual update can be performed. A valid schedule defines at
|
||||
* least one maintenance window.
|
||||
*
|
||||
* @return true if action has a maintenance schedule, else false.
|
||||
*/
|
||||
@Override
|
||||
public boolean hasMaintenanceSchedule() {
|
||||
return this.maintenanceSchedule != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method checks whether the maintenance schedule has already lapsed for
|
||||
* the action, i.e. there are no more windows available for maintenance.
|
||||
* Controller manager uses the method to check if the maintenance schedule
|
||||
* has lapsed, and automatically cancels the action if it is lapsed.
|
||||
*
|
||||
* @return true if maintenance schedule has lapsed, else false.
|
||||
*/
|
||||
@Override
|
||||
public boolean isMaintenanceScheduleLapsed() {
|
||||
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.of(maintenanceWindowTimeZone));
|
||||
final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.of(maintenanceWindowTimeZone));
|
||||
return !getScheduler().nextExecution(now.minus(getMaintenanceWindowDuration())).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* The method checks whether a maintenance window is available for the
|
||||
* action to proceed. If it is available, a 'true' value is returned. The
|
||||
* maintenance window is considered available: 1) If there is no maintenance
|
||||
* schedule at all, in which case device can start update any time after
|
||||
* download is finished; or 2) the current time is within a scheduled
|
||||
* maintenance window start and end time.
|
||||
*
|
||||
* @return true if maintenance window is available, else false.
|
||||
*/
|
||||
@Override
|
||||
public boolean isMaintenanceWindowAvailable() {
|
||||
if (!hasMaintenanceSchedule()) {
|
||||
@@ -368,10 +338,12 @@ public class JpaAction extends AbstractJpaTenantAwareBaseEntity implements Actio
|
||||
// available.
|
||||
return false;
|
||||
} else {
|
||||
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.of(maintenanceWindowTimeZone));
|
||||
if (this.getMaintenanceWindowStartTime().isPresent() && this.getMaintenanceWindowEndTime().isPresent()) {
|
||||
return now.isAfter(this.getMaintenanceWindowStartTime().get())
|
||||
&& now.isBefore(this.getMaintenanceWindowEndTime().get());
|
||||
final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.of(maintenanceWindowTimeZone));
|
||||
final Optional<ZonedDateTime> start = getMaintenanceWindowStartTime();
|
||||
final Optional<ZonedDateTime> end = getMaintenanceWindowEndTime();
|
||||
|
||||
if (start.isPresent() && end.isPresent()) {
|
||||
return now.isAfter(start.get()) && now.isBefore(end.get());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,8 @@ public class RemoteTenantAwareEventTest extends AbstractRemoteEventTest {
|
||||
final Action action = actionRepository.save(generateAction);
|
||||
|
||||
final TargetAssignDistributionSetEvent assignmentEvent = new TargetAssignDistributionSetEvent(
|
||||
action.getTenant(), dsA.getId(), Arrays.asList(action), serviceMatcher.getServiceId());
|
||||
action.getTenant(), dsA.getId(), Arrays.asList(action), serviceMatcher.getServiceId(),
|
||||
action.isMaintenanceWindowAvailable());
|
||||
|
||||
TargetAssignDistributionSetEvent underTest = (TargetAssignDistributionSetEvent) createProtoStuffEvent(
|
||||
assignmentEvent);
|
||||
|
||||
Reference in New Issue
Block a user