SDK improvements (#1693)

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2024-03-19 17:35:14 +02:00
committed by GitHub
parent 50d34dd81e
commit a37702744c
8 changed files with 90 additions and 108 deletions

View File

@@ -166,7 +166,7 @@ public class DosFilter extends OncePerRequestFilter {
} }
private static boolean handleMissingIpAddress(final HttpServletResponse response) { private static boolean handleMissingIpAddress(final HttpServletResponse response) {
log.error("Failed to get peer IP adress"); log.error("Failed to get peer IP address");
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
return false; return false;
} }

View File

@@ -9,14 +9,22 @@
*/ */
package org.eclipse.hawkbit.repository.event.remote.entity; package org.eclipse.hawkbit.repository.event.remote.entity;
import java.util.Objects; import java.io.Serial;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.eclipse.hawkbit.repository.model.Action; import org.eclipse.hawkbit.repository.model.Action;
/** /**
* Defines the remote event of creating a new {@link Action}. * Defines the remote event of creating a new {@link Action}.
*/ */
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = false)
public abstract class AbstractActionEvent extends RemoteEntityEvent<Action> { public abstract class AbstractActionEvent extends RemoteEntityEvent<Action> {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final Long targetId; private final Long targetId;
@@ -36,16 +44,11 @@ public abstract class AbstractActionEvent extends RemoteEntityEvent<Action> {
/** /**
* Constructor * Constructor
* *
* @param action * @param action the created action
* the created action * @param targetId targetId identifier (optional)
* @param targetId * @param rolloutId rollout identifier (optional)
* targetId identifier (optional) * @param rolloutGroupId rollout group identifier (optional)
* @param rolloutId * @param applicationId the origin application id
* rollout identifier (optional)
* @param rolloutGroupId
* rollout group identifier (optional)
* @param applicationId
* the origin application id
*/ */
protected AbstractActionEvent(final Action action, final Long targetId, final Long rolloutId, protected AbstractActionEvent(final Action action, final Long targetId, final Long rolloutId,
final Long rolloutGroupId, final String applicationId) { final Long rolloutGroupId, final String applicationId) {
@@ -54,34 +57,4 @@ public abstract class AbstractActionEvent extends RemoteEntityEvent<Action> {
this.rolloutId = rolloutId; this.rolloutId = rolloutId;
this.rolloutGroupId = rolloutGroupId; this.rolloutGroupId = rolloutGroupId;
} }
}
public Long getTargetId() {
return targetId;
}
public Long getRolloutId() {
return rolloutId;
}
public Long getRolloutGroupId() {
return rolloutGroupId;
}
@Override
public boolean equals(final Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
if (!super.equals(o))
return false;
final AbstractActionEvent that = (AbstractActionEvent) o;
return Objects.equals(targetId, that.targetId) && Objects.equals(rolloutId, that.rolloutId)
&& Objects.equals(rolloutGroupId, that.rolloutGroupId);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), targetId, rolloutId, rolloutGroupId);
}
}

View File

@@ -9,8 +9,11 @@
*/ */
package org.eclipse.hawkbit.repository.event.remote.entity; package org.eclipse.hawkbit.repository.event.remote.entity;
import java.io.Serial;
import java.util.Optional; import java.util.Optional;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.hawkbit.repository.event.remote.EventEntityManagerHolder; import org.eclipse.hawkbit.repository.event.remote.EventEntityManagerHolder;
import org.eclipse.hawkbit.repository.event.remote.RemoteIdEvent; import org.eclipse.hawkbit.repository.event.remote.RemoteIdEvent;
@@ -19,32 +22,24 @@ import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
/** /**
* A base definition class for remote events which contain a tenant aware base * A base definition class for remote events which contain a tenant aware base entity.
* entity.
* *
* @param <E> the type of the entity * @param <E> the type of the entity
*/ */
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Slf4j @Slf4j
public class RemoteEntityEvent<E extends TenantAwareBaseEntity> extends RemoteIdEvent { public class RemoteEntityEvent<E extends TenantAwareBaseEntity> extends RemoteIdEvent {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private transient E entity; private transient E entity;
/**
* Default constructor.
*/
protected RemoteEntityEvent() {
// for serialization libs like jackson
}
/** /**
* Constructor. * Constructor.
* *
* @param baseEntity * @param baseEntity the base entity
* the base entity * @param applicationId the origin application id
* @param applicationId
* the origin application id
*/ */
protected RemoteEntityEvent(final E baseEntity, final String applicationId) { protected RemoteEntityEvent(final E baseEntity, final String applicationId) {
super(baseEntity.getId(), baseEntity.getTenant(), baseEntity.getClass(), applicationId); super(baseEntity.getId(), baseEntity.getTenant(), baseEntity.getClass(), applicationId);
@@ -63,8 +58,8 @@ public class RemoteEntityEvent<E extends TenantAwareBaseEntity> extends RemoteId
private E reloadEntityFromRepository() { private E reloadEntityFromRepository() {
try { try {
final Class<E> clazz = (Class<E>) Class.forName(getEntityClass()); final Class<E> clazz = (Class<E>) Class.forName(getEntityClass());
return EventEntityManagerHolder.getInstance().getEventEntityManager().findEntity(getTenant(), getEntityId(), return EventEntityManagerHolder.getInstance().getEventEntityManager().findEntity(
clazz); getTenant(), getEntityId(), clazz);
} catch (final ClassNotFoundException e) { } catch (final ClassNotFoundException e) {
log.error("Cannot reload entity because class is not found", e); log.error("Cannot reload entity because class is not found", e);
} }

View File

@@ -95,7 +95,6 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
final TargetRepository targetRepository, final EntityManager entityManager, final TargetRepository targetRepository, final EntityManager entityManager,
final VirtualPropertyReplacer virtualPropertyReplacer, final RolloutStatusCache rolloutStatusCache, final VirtualPropertyReplacer virtualPropertyReplacer, final RolloutStatusCache rolloutStatusCache,
final Database database) { final Database database) {
this.rolloutGroupRepository = rolloutGroupRepository; this.rolloutGroupRepository = rolloutGroupRepository;
this.rolloutRepository = rolloutRepository; this.rolloutRepository = rolloutRepository;
this.actionRepository = actionRepository; this.actionRepository = actionRepository;
@@ -108,7 +107,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
@Override @Override
public Optional<RolloutGroup> get(final long rolloutGroupId) { public Optional<RolloutGroup> get(final long rolloutGroupId) {
return rolloutGroupRepository.findById(rolloutGroupId).map(rg -> (RolloutGroup) rg); return rolloutGroupRepository.findById(rolloutGroupId).map(RolloutGroup.class::cast);
} }
@Override @Override
@@ -146,7 +145,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
.collect(Collectors.toList()); .collect(Collectors.toList());
if (rolloutGroupIds.isEmpty()) { if (rolloutGroupIds.isEmpty()) {
// groups might already deleted, so return empty list. // groups might have been already deleted, so return empty list.
return new PageImpl<>(Collections.emptyList()); return new PageImpl<>(Collections.emptyList());
} }
@@ -155,7 +154,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
for (final JpaRolloutGroup rolloutGroup : rolloutGroups) { for (final JpaRolloutGroup rolloutGroup : rolloutGroups) {
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus( final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
allStatesForRollout.get(rolloutGroup.getId()), Long.valueOf(rolloutGroup.getTotalTargets()), allStatesForRollout.get(rolloutGroup.getId()), (long)rolloutGroup.getTotalTargets(),
rolloutGroup.getRollout().getActionType()); rolloutGroup.getRollout().getActionType());
rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus); rolloutGroup.setTotalTargetCountStatus(totalTargetCountStatus);
} }

View File

@@ -332,7 +332,7 @@ public class DdiRootController implements DdiRootControllerRestApi {
public ResponseEntity<Void> postDeploymentBaseActionFeedback(@Valid @RequestBody final DdiActionFeedback feedback, public ResponseEntity<Void> postDeploymentBaseActionFeedback(@Valid @RequestBody final DdiActionFeedback feedback,
@PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId, @PathVariable("tenant") final String tenant, @PathVariable("controllerId") final String controllerId,
@PathVariable("actionId") @NotNull final Long actionId) { @PathVariable("actionId") @NotNull final Long actionId) {
log.debug("provideBasedeploymentActionFeedback for target [{},{}]: {}", controllerId, actionId, feedback); log.debug("postDeploymentBaseActionFeedback for target [{},{}]: {}", controllerId, actionId, feedback);
final Target target = findTarget(controllerId); final Target target = findTarget(controllerId);
final Action action = findActionForTarget(actionId, target); final Action action = findActionForTarget(actionId, target);
@@ -350,7 +350,6 @@ public class DdiRootController implements DdiRootControllerRestApi {
controllerManagement.addUpdateActionStatus(generateUpdateStatus(feedback, controllerId, actionId)); controllerManagement.addUpdateActionStatus(generateUpdateStatus(feedback, controllerId, actionId));
return ResponseEntity.ok().build(); return ResponseEntity.ok().build();
} }
private ActionStatusCreate generateUpdateStatus(final DdiActionFeedback feedback, final String controllerId, private ActionStatusCreate generateUpdateStatus(final DdiActionFeedback feedback, final String controllerId,
@@ -761,5 +760,4 @@ public class DdiRootController implements DdiRootControllerRestApi {
confirmationManagement.deactivateAutoConfirmation(controllerId); confirmationManagement.deactivateAutoConfirmation(controllerId);
return new ResponseEntity<>(HttpStatus.OK); return new ResponseEntity<>(HttpStatus.OK);
} }
}
}

View File

@@ -73,11 +73,13 @@ public class DdiController {
private volatile ScheduledExecutorService executorService; private volatile ScheduledExecutorService executorService;
private volatile Long currentActionId; private volatile Long currentActionId;
private volatile Long lastActionId;
/** /**
* Creates a new device instance. * Creates a new device instance.
* *
* @param tenant the tenant of the device belongs to * @param tenant the tenant of the device belongs to
* @param controller the the controller * @param controller the controller
* @param hawkbitClient a factory for creating to {@link DdiRootControllerRestApi} (and used) * @param hawkbitClient a factory for creating to {@link DdiRootControllerRestApi} (and used)
* for communication to hawkBit * for communication to hawkBit
*/ */
@@ -102,6 +104,7 @@ public class DdiController {
public void stop() { public void stop() {
executorService = null; executorService = null;
lastActionId = null;
currentActionId = null; currentActionId = null;
} }
@@ -121,6 +124,12 @@ public class DdiController {
getRequiredLink(controllerBase, DEPLOYMENT_BASE_LINK).flatMap(this::getActionWithDeployment).ifPresentOrElse(actionWithDeployment -> { getRequiredLink(controllerBase, DEPLOYMENT_BASE_LINK).flatMap(this::getActionWithDeployment).ifPresentOrElse(actionWithDeployment -> {
final long actionId = actionWithDeployment.getKey(); final long actionId = actionWithDeployment.getKey();
if (currentActionId == null) { if (currentActionId == null) {
if (lastActionId != null && lastActionId == actionId) {
log.info(LOG_PREFIX + "Still receive the last action {}",
getTenantId(), getControllerId(), actionId);
return;
}
log.info(LOG_PREFIX + "Process action {}", getTenantId(), getControllerId(), log.info(LOG_PREFIX + "Process action {}", getTenantId(), getControllerId(),
actionId); actionId);
final DdiDeployment deployment = actionWithDeployment.getValue().getDeployment(); final DdiDeployment deployment = actionWithDeployment.getValue().getDeployment();
@@ -132,13 +141,13 @@ public class DdiController {
updateHandler.getUpdateProcessor(this, updateType, modules)); updateHandler.getUpdateProcessor(this, updateType, modules));
} else if (currentActionId != actionId) { } else if (currentActionId != actionId) {
// TODO - cancel and start new one? // TODO - cancel and start new one?
log.info(LOG_PREFIX + "Action {} is canceled while in process!", getTenantId(), log.info(LOG_PREFIX + "Action {} is canceled while in process (new {})!", getTenantId(),
getControllerId(), getCurrentActionId()); getControllerId(), currentActionId, actionId);
} // else same action - already processing } // else same action - already processing
}, () -> { }, () -> {
if (currentActionId != null) { if (currentActionId != null) {
// TODO - cancel current? // TODO - cancel current?
log.info(LOG_PREFIX + "Action {} is canceled while in process!", getTenantId(), log.info(LOG_PREFIX + "Action {} is canceled while in process (not returned)!", getTenantId(),
getControllerId(), getCurrentActionId()); getControllerId(), getCurrentActionId());
} }
}); });
@@ -218,10 +227,17 @@ public class DdiController {
void sendFeedback(final UpdateStatus updateStatus) { void sendFeedback(final UpdateStatus updateStatus) {
log.debug(LOG_PREFIX + "Send feedback {} -> {}", getTenantId(), getControllerId(), currentActionId, updateStatus); log.debug(LOG_PREFIX + "Send feedback {} -> {}", getTenantId(), getControllerId(), currentActionId, updateStatus);
getDdiApi().postDeploymentBaseActionFeedback( try {
updateStatus.feedback(), getTenantId(), getControllerId(), currentActionId); getDdiApi().postDeploymentBaseActionFeedback(updateStatus.feedback(), getTenantId(), getControllerId(),
currentActionId);
} catch (final RuntimeException e) {
log.error(LOG_PREFIX + "Failed to send feedback {} -> {}", getTenantId(), getControllerId(),
currentActionId, updateStatus, e);
}
if (updateStatus.status() == UpdateStatus.Status.SUCCESSFUL || if (updateStatus.status() == UpdateStatus.Status.SUCCESSFUL ||
updateStatus.status() == UpdateStatus.Status.ERROR) { updateStatus.status() == UpdateStatus.Status.FAILURE) {
lastActionId = currentActionId;
currentActionId = null; currentActionId = null;
} }
} }

View File

@@ -95,25 +95,27 @@ public interface UpdateHandler {
@Override @Override
public void run() { public void run() {
ddiController.sendFeedback(new UpdateStatus(UpdateStatus.Status.RUNNING, List.of("Update begins!"))); ddiController.sendFeedback(new UpdateStatus(UpdateStatus.Status.PROCEEDING, List.of("Update begin ...")));
if (!CollectionUtils.isEmpty(modules)) { if (!CollectionUtils.isEmpty(modules)) {
try { try {
final UpdateStatus updateStatus = download(); final UpdateStatus updateStatus = download();
ddiController.sendFeedback(updateStatus); ddiController.sendFeedback(updateStatus);
if (updateStatus.status() == UpdateStatus.Status.ERROR) { if (updateStatus.status() == UpdateStatus.Status.FAILURE) {
return; return;
} else { } else {
ddiController.sendFeedback(update()); if (updateType != DdiDeployment.HandlingType.SKIP) {
ddiController.sendFeedback(update());
}
} }
} finally { } finally {
cleanup(); cleanup();
} }
} }
if (updateType != DdiDeployment.HandlingType.SKIP) { if (updateType == DdiDeployment.HandlingType.SKIP) {
ddiController.sendFeedback( ddiController.sendFeedback(
new UpdateStatus(UpdateStatus.Status.SUCCESSFUL, List.of("Update complete!"))); new UpdateStatus(UpdateStatus.Status.SUCCESSFUL, List.of("Update (download-only) completed.")));
} }
} }
@@ -126,11 +128,11 @@ public interface UpdateHandler {
protected UpdateStatus download() { protected UpdateStatus download() {
ddiController.sendFeedback( ddiController.sendFeedback(
new UpdateStatus( new UpdateStatus(
UpdateStatus.Status.DOWNLOADING, UpdateStatus.Status.DOWNLOAD,
modules.stream().flatMap(mod -> mod.getArtifacts().stream()) modules.stream().flatMap(mod -> mod.getArtifacts().stream())
.map(art -> "Download starts for: " + art.getFilename() + .map(art -> "Download start for: " + art.getFilename() +
" with size " + art.getSize() + " with size " + art.getSize() +
" and hashes " + art.getHashes()) " and hashes " + art.getHashes() + " ...")
.collect(Collectors.toList()))); .collect(Collectors.toList())));
log.info(LOG_PREFIX + "Start download", ddiController.getTenantId(), ddiController.getControllerId()); log.info(LOG_PREFIX + "Start download", ddiController.getTenantId(), ddiController.getControllerId());
@@ -146,28 +148,28 @@ public interface UpdateHandler {
} }
})); }));
log.info(LOG_PREFIX + "Download complete", ddiController.getTenantId(), ddiController.getControllerId()); log.info(LOG_PREFIX + "Download complete.", ddiController.getTenantId(), ddiController.getControllerId());
final List<String> messages = new LinkedList<>(); final List<String> messages = new LinkedList<>();
messages.add("Download complete!"); messages.add("Download complete.");
updateStatusList.forEach(download -> messages.addAll(download.messages())); updateStatusList.forEach(download -> messages.addAll(download.messages()));
return new UpdateStatus( return new UpdateStatus(
updateStatusList.stream().anyMatch(status -> status.status() == UpdateStatus.Status.ERROR) ? updateStatusList.stream().anyMatch(status -> status.status() == UpdateStatus.Status.FAILURE) ?
UpdateStatus.Status.ERROR : UpdateStatus.Status.DOWNLOADED, UpdateStatus.Status.FAILURE : UpdateStatus.Status.DOWNLOADED,
messages); messages);
} }
/** /**
* Extension point. Called after all artifacts has been successfully downloadec. An overriding implementation * Extension point. Called after all artifacts has been successfully downloaded. An overriding implementation
* may get the {@link #downloads} map and apply them * may get the {@link #downloads} map and apply them
*/ */
protected UpdateStatus update() { protected UpdateStatus update() {
log.info(LOG_PREFIX + "Updated", ddiController.getTenantId(), ddiController.getControllerId()); log.info(LOG_PREFIX + "Updated", ddiController.getTenantId(), ddiController.getControllerId());
return new UpdateStatus(UpdateStatus.Status.SUCCESSFUL, List.of("Update applied")); return new UpdateStatus(UpdateStatus.Status.SUCCESSFUL, List.of("Update complete."));
} }
/** /**
* Extension point. Called after download and update has been finished. By default it deletes all downloaded * Extension point. Called after download and update has been finished. By default, it deletes all downloaded
* files (if any). * files (if any).
*/ */
protected void cleanup() { protected void cleanup() {
@@ -189,8 +191,7 @@ public interface UpdateHandler {
artifact.getLink("download").ifPresentOrElse( artifact.getLink("download").ifPresentOrElse(
// HTTPS // HTTPS
link -> status.add(downloadUrl(link.getHref(), gatewayToken, targetToken, link -> status.add(downloadUrl(link.getHref(), gatewayToken, targetToken,
artifact.getHashes(), artifact.getSize())) artifact.getHashes(), artifact.getSize())),
,
// HTTP // HTTP
() -> status.add(downloadUrl( () -> status.add(downloadUrl(
artifact.getLink("download-http") artifact.getLink("download-http")
@@ -216,7 +217,7 @@ public interface UpdateHandler {
log.error(LOG_PREFIX + "Failed to download {}", log.error(LOG_PREFIX + "Failed to download {}",
ddiController.getTenantId(), ddiController.getControllerId(), url, e); ddiController.getTenantId(), ddiController.getControllerId(), url, e);
return new UpdateStatus( return new UpdateStatus(
UpdateStatus.Status.ERROR, UpdateStatus.Status.FAILURE,
List.of("Failed to download " + url + ": " + e.getMessage())); List.of("Failed to download " + url + ": " + e.getMessage()));
} }
} }
@@ -273,7 +274,7 @@ public interface UpdateHandler {
ddiController.getTenantId(), ddiController.getControllerId()); ddiController.getTenantId(), ddiController.getControllerId());
} }
downloadHandler.finished(ArtifactHandler.DownloadHandler.Status.ERROR); downloadHandler.finished(ArtifactHandler.DownloadHandler.Status.ERROR);
return new UpdateStatus(UpdateStatus.Status.ERROR, List.of(message)); return new UpdateStatus(UpdateStatus.Status.FAILURE, List.of(message));
} }
}); });
} }

View File

@@ -23,6 +23,21 @@ public record UpdateStatus(Status status, List<String> messages) {
*/ */
public enum Status { public enum Status {
/**
* Update is running (intermediate status).
*/
PROCEEDING(DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.NONE, null),
/**
* Device starts to download.
*/
DOWNLOAD(DdiStatus.ExecutionStatus.DOWNLOAD, DdiResult.FinalResult.NONE, null),
/**
* Device is finished with downloading.
*/
DOWNLOADED(DdiStatus.ExecutionStatus.DOWNLOADED, DdiResult.FinalResult.NONE, null),
/** /**
* Update has been successful and response the successful update. * Update has been successful and response the successful update.
*/ */
@@ -31,22 +46,7 @@ public record UpdateStatus(Status status, List<String> messages) {
/** /**
* Update has been not successful and response the error update. * Update has been not successful and response the error update.
*/ */
ERROR(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.FAILURE, null), FAILURE(DdiStatus.ExecutionStatus.CLOSED, DdiResult.FinalResult.FAILURE, null);
/**
* Update is running (intermediate status).
*/
RUNNING(DdiStatus.ExecutionStatus.PROCEEDING, DdiResult.FinalResult.NONE, null),
/**
* Device starts to download.
*/
DOWNLOADING(DdiStatus.ExecutionStatus.DOWNLOAD, DdiResult.FinalResult.NONE, null),
/**
* Device is finished with downloading.
*/
DOWNLOADED(DdiStatus.ExecutionStatus.DOWNLOADED, DdiResult.FinalResult.NONE, null);
private final DdiStatus.ExecutionStatus executionStatus; private final DdiStatus.ExecutionStatus executionStatus;
private final DdiResult.FinalResult finalResult; private final DdiResult.FinalResult finalResult;