Feature offline deployments (#563)
* Repository support offline deployments. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Add offline assignment to Management API. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * DsAssignmentStrategy introduced. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Fixed JavaDoc. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com> * Readibility improved. Signed-off-by: kaizimmerm <kai.zimmermann@bosch-si.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import java.util.Optional;
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
@@ -28,6 +29,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -69,7 +71,6 @@ public interface DeploymentManagement {
|
||||
DistributionSetAssignmentResult assignDistributionSet(@NotNull Long dsID, @NotNull ActionType actionType,
|
||||
long forcedTimestamp, @NotEmpty Collection<String> controllerIDs);
|
||||
|
||||
|
||||
/**
|
||||
* method assigns the {@link DistributionSet} to all {@link Target}s by
|
||||
* their IDs with a specific {@link ActionType} and {@code forcetime}.
|
||||
@@ -116,6 +117,38 @@ public interface DeploymentManagement {
|
||||
DistributionSetAssignmentResult assignDistributionSet(@NotNull Long dsID,
|
||||
@NotEmpty Collection<TargetWithActionType> targets, String actionMessage);
|
||||
|
||||
/**
|
||||
* Method registers an "offline" assignment, i.e. adds a completed action
|
||||
* for the given {@link DistributionSet} to the given {@link Target}s.
|
||||
*
|
||||
* The handling differs to hawkBit managed updates my means that:<br/>
|
||||
*
|
||||
* <ol type="A">
|
||||
* <li>it ignores targets completely that are in
|
||||
* {@link TargetUpdateStatus#PENDING}.</li>
|
||||
* <li>it creates completed actions.</li>
|
||||
* <li>sets both installed and assigned DS on the target and switches the
|
||||
* status to {@link TargetUpdateStatus#IN_SYNC}.</li>
|
||||
* <li>does not send a {@link TargetAssignDistributionSetEvent}.</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param dsID
|
||||
* the ID of the distribution set that was assigned
|
||||
* @param controllerIDs
|
||||
* a list of IDs of the targets that where assigned
|
||||
* @return the assignment result
|
||||
*
|
||||
* @throws IncompleteDistributionSetException
|
||||
* if mandatory {@link SoftwareModuleType} are not assigned as
|
||||
* defined by the {@link DistributionSetType}.
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* if either provided {@link DistributionSet} or {@link Target}s
|
||||
* do not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||
DistributionSetAssignmentResult offlineAssignedDistributionSet(Long dsID, Collection<String> controllerIDs);
|
||||
|
||||
/**
|
||||
* Cancels given {@link Action} for given {@link Target}. The method will
|
||||
* immediately add a {@link Status#CANCELED} status to the action. However,
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
||||
/**
|
||||
* {@link DistributionSet} to {@link Target} assignment strategy as utility for
|
||||
* {@link JpaDeploymentManagement}.
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractDsAssignmentStrategy {
|
||||
|
||||
protected final TargetRepository targetRepository;
|
||||
protected final AfterTransactionCommitExecutor afterCommit;
|
||||
protected final ApplicationEventPublisher eventPublisher;
|
||||
protected final ApplicationContext applicationContext;
|
||||
private final ActionRepository actionRepository;
|
||||
private final ActionStatusRepository actionStatusRepository;
|
||||
|
||||
AbstractDsAssignmentStrategy(final TargetRepository targetRepository,
|
||||
final AfterTransactionCommitExecutor afterCommit, final ApplicationEventPublisher eventPublisher,
|
||||
final ApplicationContext applicationContext, final ActionRepository actionRepository,
|
||||
final ActionStatusRepository actionStatusRepository) {
|
||||
this.targetRepository = targetRepository;
|
||||
this.afterCommit = afterCommit;
|
||||
this.eventPublisher = eventPublisher;
|
||||
this.applicationContext = applicationContext;
|
||||
this.actionRepository = actionRepository;
|
||||
this.actionStatusRepository = actionStatusRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find targets to be considered for assignment.
|
||||
*
|
||||
* @param controllerIDs
|
||||
* as provided by repository caller
|
||||
* @param distributionSetId
|
||||
* to assign
|
||||
* @return list of targets up to {@link Constants#MAX_ENTRIES_IN_STATEMENT}
|
||||
*/
|
||||
abstract List<JpaTarget> findTargetsForAssignment(final List<String> controllerIDs, final long distributionSetId);
|
||||
|
||||
/**
|
||||
* Update status and DS fields of given target.
|
||||
*
|
||||
* @param distributionSet
|
||||
* to set
|
||||
* @param targetIds
|
||||
* to change
|
||||
* @param currentUser
|
||||
* for auditing
|
||||
*/
|
||||
abstract void updateTargetStatus(final JpaDistributionSet distributionSet, final List<List<Long>> targetIds,
|
||||
final String currentUser);
|
||||
|
||||
/**
|
||||
* Cancels actions that can be canceled (i.e.
|
||||
* {@link DistributionSet#isRequiredMigrationStep() is <code>false</code>})
|
||||
* as a result of the new assignment and returns all {@link Target}s where
|
||||
* such actions existed.
|
||||
*
|
||||
* @param targetIds
|
||||
* to cancel actions for
|
||||
* @return {@link Set} of {@link Target#getId()}s
|
||||
*/
|
||||
abstract Set<Long> findTargetIdsToCancel(List<List<Long>> targetIds);
|
||||
|
||||
/**
|
||||
* Handles event sending related to the assignment.
|
||||
*
|
||||
* @param targets
|
||||
* to send events for
|
||||
* @param targetIdsCancelList
|
||||
* targets where an action was canceled
|
||||
* @param controllerIdsToActions
|
||||
* mapping of {@link Target#getControllerId()} to new
|
||||
* {@link Action} that was created as part of the assignment.
|
||||
*/
|
||||
abstract void sendAssignmentEvents(final List<JpaTarget> targets, final Set<Long> targetIdsCancelList,
|
||||
final Map<String, JpaAction> controllerIdsToActions);
|
||||
|
||||
protected void sendTargetAssignDistributionSetEvent(final Action action) {
|
||||
afterCommit.afterCommit(() -> eventPublisher
|
||||
.publishEvent(new TargetAssignDistributionSetEvent(action, applicationContext.getId())));
|
||||
}
|
||||
|
||||
protected void sendTargetUpdatedEvent(final JpaTarget target) {
|
||||
afterCommit.afterCommit(
|
||||
() -> eventPublisher.publishEvent(new TargetUpdatedEvent(target, applicationContext.getId())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes {@link Action}s that are no longer necessary and sends
|
||||
* cancellations to the controller.
|
||||
*
|
||||
* @param targetsIds
|
||||
* to override {@link Action}s
|
||||
*/
|
||||
protected List<Long> overrideObsoleteUpdateActions(final Collection<Long> targetsIds) {
|
||||
|
||||
// Figure out if there are potential target/action combinations that
|
||||
// need to be considered for cancellation
|
||||
final List<JpaAction> activeActions = actionRepository
|
||||
.findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep(targetsIds,
|
||||
Action.Status.CANCELING);
|
||||
|
||||
return activeActions.stream().map(action -> {
|
||||
action.setStatus(Status.CANCELING);
|
||||
|
||||
// document that the status has been retrieved
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
|
||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "cancel obsolete action due to new update"));
|
||||
actionRepository.save(action);
|
||||
|
||||
cancelAssignDistributionSetEvent(action.getTarget(), action.getId());
|
||||
|
||||
return action.getTarget().getId();
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the {@link CancelTargetAssignmentEvent} for a specific target to
|
||||
* the eventPublisher.
|
||||
*
|
||||
* @param target
|
||||
* the Target which has been assigned to a distribution set
|
||||
* @param actionId
|
||||
* the action id of the assignment
|
||||
*/
|
||||
void cancelAssignDistributionSetEvent(final Target target, final Long actionId) {
|
||||
afterCommit.afterCommit(() -> eventPublisher
|
||||
.publishEvent(new CancelTargetAssignmentEvent(target, actionId, applicationContext.getId())));
|
||||
}
|
||||
|
||||
JpaAction createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap, final JpaTarget target,
|
||||
final JpaDistributionSet set) {
|
||||
final JpaAction actionForTarget = new JpaAction();
|
||||
final TargetWithActionType targetWithActionType = targetsWithActionMap.get(target.getControllerId());
|
||||
actionForTarget.setActionType(targetWithActionType.getActionType());
|
||||
actionForTarget.setForcedTime(targetWithActionType.getForceTime());
|
||||
actionForTarget.setActive(true);
|
||||
actionForTarget.setTarget(target);
|
||||
actionForTarget.setDistributionSet(set);
|
||||
return actionForTarget;
|
||||
}
|
||||
|
||||
JpaActionStatus createActionStatus(final JpaAction action, final String actionMessage) {
|
||||
final JpaActionStatus actionStatus = new JpaActionStatus();
|
||||
actionStatus.setAction(action);
|
||||
actionStatus.setOccurredAt(action.getCreatedAt());
|
||||
|
||||
if (actionMessage != null) {
|
||||
actionStatus.addMessage(actionMessage);
|
||||
}
|
||||
|
||||
return actionStatus;
|
||||
}
|
||||
}
|
||||
@@ -31,8 +31,6 @@ import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.CancelTargetAssignmentEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException;
|
||||
@@ -47,7 +45,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
@@ -62,7 +59,6 @@ import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
@@ -99,51 +95,70 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
*/
|
||||
private static final int ACTION_PAGE_LIMIT = 1000;
|
||||
|
||||
@Autowired
|
||||
private EntityManager entityManager;
|
||||
private final EntityManager entityManager;
|
||||
private final ActionRepository actionRepository;
|
||||
private final DistributionSetRepository distributionSetRepository;
|
||||
private final TargetRepository targetRepository;
|
||||
private final ActionStatusRepository actionStatusRepository;
|
||||
private final TargetManagement targetManagement;
|
||||
private final AuditorAware<String> auditorProvider;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
private final ApplicationContext applicationContext;
|
||||
private final AfterTransactionCommitExecutor afterCommit;
|
||||
private final VirtualPropertyReplacer virtualPropertyReplacer;
|
||||
private final PlatformTransactionManager txManager;
|
||||
private final OnlineDsAssignmentStrategy onlineDsAssignmentStrategy;
|
||||
private final OfflineDsAssignmentStrategy offlineDsAssignmentStrategy;
|
||||
|
||||
@Autowired
|
||||
private ActionRepository actionRepository;
|
||||
JpaDeploymentManagement(final EntityManager entityManager, final ActionRepository actionRepository,
|
||||
final DistributionSetRepository distributionSetRepository, final TargetRepository targetRepository,
|
||||
final ActionStatusRepository actionStatusRepository, final TargetManagement targetManagement,
|
||||
final AuditorAware<String> auditorProvider, final ApplicationEventPublisher eventPublisher,
|
||||
final ApplicationContext applicationContext, final AfterTransactionCommitExecutor afterCommit,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager) {
|
||||
this.entityManager = entityManager;
|
||||
this.actionRepository = actionRepository;
|
||||
this.distributionSetRepository = distributionSetRepository;
|
||||
this.targetRepository = targetRepository;
|
||||
this.actionStatusRepository = actionStatusRepository;
|
||||
this.targetManagement = targetManagement;
|
||||
this.auditorProvider = auditorProvider;
|
||||
this.eventPublisher = eventPublisher;
|
||||
this.applicationContext = applicationContext;
|
||||
this.afterCommit = afterCommit;
|
||||
this.virtualPropertyReplacer = virtualPropertyReplacer;
|
||||
this.txManager = txManager;
|
||||
onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, afterCommit, eventPublisher,
|
||||
applicationContext, actionRepository, actionStatusRepository);
|
||||
offlineDsAssignmentStrategy = new OfflineDsAssignmentStrategy(targetRepository, afterCommit, eventPublisher,
|
||||
applicationContext, actionRepository, actionStatusRepository);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private DistributionSetRepository distributionSetRepository;
|
||||
|
||||
@Autowired
|
||||
private TargetRepository targetRepository;
|
||||
|
||||
@Autowired
|
||||
private ActionStatusRepository actionStatusRepository;
|
||||
|
||||
@Autowired
|
||||
private TargetManagement targetManagement;
|
||||
|
||||
@Autowired
|
||||
private AuditorAware<String> auditorProvider;
|
||||
|
||||
@Autowired
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
private AfterTransactionCommitExecutor afterCommit;
|
||||
|
||||
@Autowired
|
||||
private VirtualPropertyReplacer virtualPropertyReplacer;
|
||||
|
||||
@Autowired
|
||||
private PlatformTransactionManager txManager;
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSetAssignmentResult offlineAssignedDistributionSet(final Long dsID,
|
||||
final Collection<String> controllerIDs) {
|
||||
return assignDistributionSetToTargets(dsID,
|
||||
controllerIDs.stream()
|
||||
.map(controllerId -> new TargetWithActionType(controllerId, ActionType.FORCED, -1))
|
||||
.collect(Collectors.toList()),
|
||||
null, offlineDsAssignmentStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID, final ActionType actionType,
|
||||
final long forcedTimestamp, final Collection<String> targetIDs) {
|
||||
final long forcedTimestamp, final Collection<String> controllerIDs) {
|
||||
|
||||
return assignDistributionSetToTargets(dsID, targetIDs.stream()
|
||||
.map(t -> new TargetWithActionType(t, actionType, forcedTimestamp)).collect(Collectors.toList()), null);
|
||||
return assignDistributionSetToTargets(dsID,
|
||||
controllerIDs.stream()
|
||||
.map(controllerId -> new TargetWithActionType(controllerId, actionType, forcedTimestamp))
|
||||
.collect(Collectors.toList()),
|
||||
null, onlineDsAssignmentStrategy);
|
||||
|
||||
}
|
||||
|
||||
@@ -154,7 +169,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
|
||||
final Collection<TargetWithActionType> targets) {
|
||||
|
||||
return assignDistributionSetToTargets(dsID, targets, null);
|
||||
return assignDistributionSetToTargets(dsID, targets, null, onlineDsAssignmentStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -164,12 +179,22 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
|
||||
final Collection<TargetWithActionType> targets, final String actionMessage) {
|
||||
|
||||
return assignDistributionSetToTargets(dsID, targets, actionMessage);
|
||||
return assignDistributionSetToTargets(dsID, targets, actionMessage, onlineDsAssignmentStrategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* method assigns the {@link DistributionSet} to all {@link Target}s by
|
||||
* their IDs with a specific {@link ActionType} and {@code forcetime}.
|
||||
*
|
||||
*
|
||||
* In case the update was executed offline (i.e. not managed by hawkBit) the
|
||||
* handling differs my means that:<br/>
|
||||
* A. it ignores targets completely that are in
|
||||
* {@link TargetUpdateStatus#PENDING}.<br/>
|
||||
* B. it created completed actions.<br/>
|
||||
* C. sets both installed and assigned DS on the target and switches the
|
||||
* status to {@link TargetUpdateStatus#IN_SYNC} <br/>
|
||||
* D. does not send a {@link TargetAssignDistributionSetEvent}.<br/>
|
||||
*
|
||||
* @param dsID
|
||||
* the ID of the distribution set to assign
|
||||
@@ -177,6 +202,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
* a list of all targets and their action type
|
||||
* @param actionMessage
|
||||
* an optional message to be written into the action status
|
||||
* @param offline
|
||||
* to <code>true</code> in offline case
|
||||
* @return the assignment result
|
||||
*
|
||||
* @throw IncompleteDistributionSetException if mandatory
|
||||
@@ -184,7 +211,8 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
* {@link DistributionSetType}.
|
||||
*/
|
||||
private DistributionSetAssignmentResult assignDistributionSetToTargets(final Long dsID,
|
||||
final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage) {
|
||||
final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy) {
|
||||
|
||||
final JpaDistributionSet set = distributionSetRepository.findOne(dsID);
|
||||
if (set == null) {
|
||||
@@ -209,10 +237,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
// maximum 1000 elements, so we need to split the entries here and
|
||||
// execute multiple statements we take the target only into account if
|
||||
// the requested operation is no duplicate of a previous one
|
||||
final List<JpaTarget> targets = Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream()
|
||||
.map(ids -> targetRepository
|
||||
.findAll(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, set.getId())))
|
||||
.flatMap(List::stream).collect(Collectors.toList());
|
||||
final List<JpaTarget> targets = assignmentStrategy.findTargetsForAssignment(controllerIDs, set.getId());
|
||||
|
||||
if (targets.isEmpty()) {
|
||||
// detaching as it is not necessary to persist the set itself
|
||||
@@ -229,8 +254,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
// need to remember which one we have been switched to canceling state
|
||||
// because for targets which we have changed to canceling we don't want
|
||||
// to publish the new action update event.
|
||||
final Set<Long> targetIdsCancellList = targetIds.stream().map(this::overrideObsoleteUpdateActions)
|
||||
.flatMap(Collection::stream).collect(Collectors.toSet());
|
||||
final Set<Long> targetIdsCancellList = assignmentStrategy.findTargetIdsToCancel(targetIds);
|
||||
|
||||
// cancel all scheduled actions which are in-active, these actions were
|
||||
// not active before and the manual assignment which has been done
|
||||
@@ -245,24 +269,26 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
currentUser = null;
|
||||
}
|
||||
|
||||
targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSetAndUpdateStatus(TargetUpdateStatus.PENDING,
|
||||
set, System.currentTimeMillis(), currentUser, tIds));
|
||||
assignmentStrategy.updateTargetStatus(set, targetIds, currentUser);
|
||||
|
||||
final Map<String, JpaAction> targetIdsToActions = targets.stream()
|
||||
.map(t -> actionRepository.save(createTargetAction(targetsWithActionMap, t, set)))
|
||||
.map(t -> actionRepository.save(assignmentStrategy.createTargetAction(targetsWithActionMap, t, set)))
|
||||
.collect(Collectors.toMap(a -> a.getTarget().getControllerId(), Function.identity()));
|
||||
|
||||
// create initial action status when action is created so we remember
|
||||
// the initial running status because we will change the status
|
||||
// of the action itself and with this action status we have a nicer
|
||||
// action history.
|
||||
targetIdsToActions.values().forEach(action -> setRunningActionStatus(action, actionMessage));
|
||||
actionStatusRepository.save(targetIdsToActions.values().stream()
|
||||
.map(action -> assignmentStrategy.createActionStatus(action, actionMessage))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
// detaching as it is not necessary to persist the set itself
|
||||
entityManager.detach(set);
|
||||
// detaching as the entity has been updated by the JPQL query above
|
||||
targets.forEach(entityManager::detach);
|
||||
|
||||
sendAssignmentEvents(targets, targetIdsCancellList, targetIdsToActions);
|
||||
assignmentStrategy.sendAssignmentEvents(targets, targetIdsCancellList, targetIdsToActions);
|
||||
|
||||
return new DistributionSetAssignmentResult(
|
||||
targets.stream().map(Target::getControllerId).collect(Collectors.toList()), targets.size(),
|
||||
@@ -270,77 +296,6 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
targetManagement);
|
||||
}
|
||||
|
||||
private void sendAssignmentEvents(final List<JpaTarget> targets, final Set<Long> targetIdsCancellList,
|
||||
final Map<String, JpaAction> targetIdsToActions) {
|
||||
|
||||
targets.forEach(target -> {
|
||||
sendTargetUpdatedEvent(target);
|
||||
if (targetIdsCancellList.contains(target.getId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendTargetAssignDistributionSetEvent(targetIdsToActions.get(target.getControllerId()));
|
||||
});
|
||||
}
|
||||
|
||||
private static JpaAction createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap,
|
||||
final JpaTarget target, final JpaDistributionSet set) {
|
||||
final JpaAction actionForTarget = new JpaAction();
|
||||
final TargetWithActionType targetWithActionType = targetsWithActionMap.get(target.getControllerId());
|
||||
actionForTarget.setActionType(targetWithActionType.getActionType());
|
||||
actionForTarget.setForcedTime(targetWithActionType.getForceTime());
|
||||
actionForTarget.setActive(true);
|
||||
actionForTarget.setStatus(Status.RUNNING);
|
||||
actionForTarget.setTarget(target);
|
||||
actionForTarget.setDistributionSet(set);
|
||||
return actionForTarget;
|
||||
}
|
||||
|
||||
private void sendTargetAssignDistributionSetEvent(final Action action) {
|
||||
afterCommit.afterCommit(() -> eventPublisher
|
||||
.publishEvent(new TargetAssignDistributionSetEvent(action, applicationContext.getId())));
|
||||
}
|
||||
|
||||
private void sendTargetUpdatedEvent(final JpaTarget target) {
|
||||
|
||||
// Update is not available in the object as the update was executed
|
||||
// through JQL
|
||||
target.setUpdateStatus(TargetUpdateStatus.PENDING);
|
||||
|
||||
afterCommit.afterCommit(
|
||||
() -> eventPublisher.publishEvent(new TargetUpdatedEvent(target, applicationContext.getId())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes {@link Action}s that are no longer necessary and sends
|
||||
* cancellations to the controller.
|
||||
*
|
||||
* @param targetsIds
|
||||
* to override {@link Action}s
|
||||
*/
|
||||
private List<Long> overrideObsoleteUpdateActions(final Collection<Long> targetsIds) {
|
||||
|
||||
// Figure out if there are potential target/action combinations that
|
||||
// need to be considered for cancellation
|
||||
final List<JpaAction> activeActions = actionRepository
|
||||
.findByActiveAndTargetIdInAndActionStatusNotEqualToAndDistributionSetRequiredMigrationStep(targetsIds,
|
||||
Action.Status.CANCELING);
|
||||
|
||||
return activeActions.stream().map(action -> {
|
||||
action.setStatus(Status.CANCELING);
|
||||
// document that the status has been retrieved
|
||||
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
|
||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "cancel obsolete action due to new update"));
|
||||
actionRepository.save(action);
|
||||
|
||||
cancelAssignDistributionSetEvent(action.getTarget(), action.getId());
|
||||
|
||||
return action.getTarget().getId();
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(include = {
|
||||
@@ -363,7 +318,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
|
||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "manual cancelation requested"));
|
||||
final Action saveAction = actionRepository.save(action);
|
||||
cancelAssignDistributionSetEvent(action.getTarget(), action.getId());
|
||||
onlineDsAssignmentStrategy.cancelAssignDistributionSetEvent(action.getTarget(), action.getId());
|
||||
|
||||
return saveAction;
|
||||
} else {
|
||||
@@ -371,20 +326,6 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the {@link CancelTargetAssignmentEvent} for a specific target to
|
||||
* the eventPublisher.
|
||||
*
|
||||
* @param target
|
||||
* the Target which has been assigned to a distribution set
|
||||
* @param actionId
|
||||
* the action id of the assignment
|
||||
*/
|
||||
private void cancelAssignDistributionSetEvent(final Target target, final Long actionId) {
|
||||
afterCommit.afterCommit(() -> eventPublisher
|
||||
.publishEvent(new CancelTargetAssignmentEvent(target, actionId, applicationContext.getId())));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
@Retryable(include = {
|
||||
@@ -471,14 +412,14 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
}
|
||||
|
||||
// check if we need to override running update actions
|
||||
final List<Long> overrideObsoleteUpdateActions = overrideObsoleteUpdateActions(
|
||||
Collections.singletonList(action.getTarget().getId()));
|
||||
final List<Long> overrideObsoleteUpdateActions = onlineDsAssignmentStrategy
|
||||
.overrideObsoleteUpdateActions(Collections.singletonList(action.getTarget().getId()));
|
||||
|
||||
action.setActive(true);
|
||||
action.setStatus(Status.RUNNING);
|
||||
final JpaAction savedAction = actionRepository.save(action);
|
||||
|
||||
setRunningActionStatus(savedAction, null);
|
||||
actionStatusRepository.save(onlineDsAssignmentStrategy.createActionStatus(savedAction, null));
|
||||
|
||||
target = (JpaTarget) entityManager.merge(savedAction.getTarget());
|
||||
|
||||
@@ -494,18 +435,6 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
}
|
||||
}
|
||||
|
||||
private void setRunningActionStatus(final JpaAction action, final String actionMessage) {
|
||||
final JpaActionStatus actionStatus = new JpaActionStatus();
|
||||
actionStatus.setAction(action);
|
||||
actionStatus.setOccurredAt(action.getCreatedAt());
|
||||
actionStatus.setStatus(Status.RUNNING);
|
||||
if (actionMessage != null) {
|
||||
actionStatus.addMessage(actionMessage);
|
||||
}
|
||||
|
||||
actionStatusRepository.save(actionStatus);
|
||||
}
|
||||
|
||||
private void setSkipActionStatus(final JpaAction action) {
|
||||
final JpaActionStatus actionStatus = new JpaActionStatus();
|
||||
actionStatus.setAction(action);
|
||||
@@ -683,4 +612,5 @@ public class JpaDeploymentManagement implements DeploymentManagement {
|
||||
|
||||
return distributionSetRepository.findInstalledAtTarget(controllerId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* AbstractDsAssignmentStrategy for offline assignments, i.e. not managed by
|
||||
* hawkBit.
|
||||
*
|
||||
*/
|
||||
public class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
|
||||
OfflineDsAssignmentStrategy(final TargetRepository targetRepository,
|
||||
final AfterTransactionCommitExecutor afterCommit, final ApplicationEventPublisher eventPublisher,
|
||||
final ApplicationContext applicationContext, final ActionRepository actionRepository,
|
||||
final ActionStatusRepository actionStatusRepository) {
|
||||
super(targetRepository, afterCommit, eventPublisher, applicationContext, actionRepository,
|
||||
actionStatusRepository);
|
||||
}
|
||||
|
||||
@Override
|
||||
void sendAssignmentEvents(final List<JpaTarget> targets, final Set<Long> targetIdsCancellList,
|
||||
final Map<String, JpaAction> targetIdsToActions) {
|
||||
|
||||
targets.forEach(target -> {
|
||||
target.setUpdateStatus(TargetUpdateStatus.IN_SYNC);
|
||||
sendTargetUpdatedEvent(target);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<JpaTarget> findTargetsForAssignment(final List<String> controllerIDs, final long setId) {
|
||||
return Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream()
|
||||
.map(ids -> targetRepository.findAll(SpecificationsBuilder.combineWithAnd(
|
||||
Arrays.asList(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, setId),
|
||||
TargetSpecifications.notEqualToTargetUpdateStatus(TargetUpdateStatus.PENDING)))))
|
||||
.flatMap(List::stream).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Long> findTargetIdsToCancel(final List<List<Long>> targetIds) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateTargetStatus(final JpaDistributionSet set, final List<List<Long>> targetIds, final String currentUser) {
|
||||
targetIds.forEach(tIds -> targetRepository.setAssignedAndInstalledDistributionSetAndUpdateStatus(
|
||||
TargetUpdateStatus.IN_SYNC, set, System.currentTimeMillis(), currentUser, tIds));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JpaAction createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap,
|
||||
final JpaTarget target, final JpaDistributionSet set) {
|
||||
final JpaAction result = super.createTargetAction(targetsWithActionMap, target, set);
|
||||
result.setStatus(Status.FINISHED);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JpaActionStatus createActionStatus(final JpaAction action, final String actionMessage) {
|
||||
final JpaActionStatus result = super.createActionStatus(action, actionMessage);
|
||||
result.setStatus(Status.FINISHED);
|
||||
result.addMessage(RepositoryConstants.SERVER_MESSAGE_PREFIX + "Action reported as offline deployment");
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* AbstractDsAssignmentStrategy for online assignments, i.e. managed by hawkBit.
|
||||
*
|
||||
*/
|
||||
public class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
|
||||
OnlineDsAssignmentStrategy(final TargetRepository targetRepository,
|
||||
final AfterTransactionCommitExecutor afterCommit, final ApplicationEventPublisher eventPublisher,
|
||||
final ApplicationContext applicationContext, final ActionRepository actionRepository,
|
||||
final ActionStatusRepository actionStatusRepository) {
|
||||
super(targetRepository, afterCommit, eventPublisher, applicationContext, actionRepository,
|
||||
actionStatusRepository);
|
||||
}
|
||||
|
||||
@Override
|
||||
void sendAssignmentEvents(final List<JpaTarget> targets, final Set<Long> targetIdsCancellList,
|
||||
final Map<String, JpaAction> targetIdsToActions) {
|
||||
|
||||
targets.forEach(target -> {
|
||||
target.setUpdateStatus(TargetUpdateStatus.PENDING);
|
||||
sendTargetUpdatedEvent(target);
|
||||
if (targetIdsCancellList.contains(target.getId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendTargetAssignDistributionSetEvent(targetIdsToActions.get(target.getControllerId()));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
List<JpaTarget> findTargetsForAssignment(final List<String> controllerIDs, final long setId) {
|
||||
return Lists.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream()
|
||||
.map(ids -> targetRepository
|
||||
.findAll(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, setId)))
|
||||
.flatMap(List::stream).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
Set<Long> findTargetIdsToCancel(final List<List<Long>> targetIds) {
|
||||
return targetIds.stream().map(this::overrideObsoleteUpdateActions).flatMap(Collection::stream)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
void updateTargetStatus(final JpaDistributionSet set, final List<List<Long>> targetIds, final String currentUser) {
|
||||
targetIds.forEach(tIds -> targetRepository.setAssignedDistributionSetAndUpdateStatus(TargetUpdateStatus.PENDING,
|
||||
set, System.currentTimeMillis(), currentUser, tIds));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
JpaAction createTargetAction(final Map<String, TargetWithActionType> targetsWithActionMap, final JpaTarget target,
|
||||
final JpaDistributionSet set) {
|
||||
final JpaAction result = super.createTargetAction(targetsWithActionMap, target, set);
|
||||
result.setStatus(Status.RUNNING);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
JpaActionStatus createActionStatus(final JpaAction action, final String actionMessage) {
|
||||
final JpaActionStatus result = super.createActionStatus(action, actionMessage);
|
||||
result.setStatus(Status.RUNNING);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -50,6 +50,7 @@ import org.eclipse.hawkbit.repository.jpa.builder.JpaSoftwareModuleBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager;
|
||||
import org.eclipse.hawkbit.repository.jpa.event.JpaEventEntityManager;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.SecurityTokenGeneratorHolder;
|
||||
@@ -87,6 +88,7 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.integration.support.locks.LockRegistry;
|
||||
@@ -496,8 +498,16 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
DeploymentManagement deploymentManagement() {
|
||||
return new JpaDeploymentManagement();
|
||||
DeploymentManagement deploymentManagement(final EntityManager entityManager,
|
||||
final ActionRepository actionRepository, final DistributionSetRepository distributionSetRepository,
|
||||
final TargetRepository targetRepository, final ActionStatusRepository actionStatusRepository,
|
||||
final TargetManagement targetManagement, final AuditorAware<String> auditorProvider,
|
||||
final ApplicationEventPublisher eventPublisher, final ApplicationContext applicationContext,
|
||||
final AfterTransactionCommitExecutor afterCommit, final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||
final PlatformTransactionManager txManager) {
|
||||
return new JpaDeploymentManagement(entityManager, actionRepository, distributionSetRepository, targetRepository,
|
||||
actionStatusRepository, targetManagement, auditorProvider, eventPublisher, applicationContext,
|
||||
afterCommit, virtualPropertyReplacer, txManager);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -58,6 +58,29 @@ public interface TargetRepository extends BaseEntityRepository<JpaTarget, Long>,
|
||||
@Param("set") JpaDistributionSet set, @Param("lastModifiedAt") Long modifiedAt,
|
||||
@Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets);
|
||||
|
||||
/**
|
||||
* Sets {@link JpaTarget#getAssignedDistributionSet()},
|
||||
* {@link JpaTarget#getInstalledDistributionSet()} and
|
||||
* {@link JpaTarget#getInstallationDate()}
|
||||
*
|
||||
* @param set
|
||||
* to use
|
||||
* @param status
|
||||
* to set
|
||||
* @param modifiedAt
|
||||
* current time
|
||||
* @param modifiedBy
|
||||
* current auditor
|
||||
* @param targets
|
||||
* to update
|
||||
*/
|
||||
@Modifying
|
||||
@Transactional
|
||||
@Query("UPDATE JpaTarget t SET t.assignedDistributionSet = :set, t.installedDistributionSet = :set, t.installationDate = :lastModifiedAt, t.lastModifiedAt = :lastModifiedAt, t.lastModifiedBy = :lastModifiedBy, t.updateStatus = :status WHERE t.id IN :targets")
|
||||
void setAssignedAndInstalledDistributionSetAndUpdateStatus(@Param("status") TargetUpdateStatus status,
|
||||
@Param("set") JpaDistributionSet set, @Param("lastModifiedAt") Long modifiedAt,
|
||||
@Param("lastModifiedBy") String modifiedBy, @Param("targets") Collection<Long> targets);
|
||||
|
||||
/**
|
||||
* Loads {@link Target} by given ID.
|
||||
*
|
||||
|
||||
@@ -97,6 +97,19 @@ public final class TargetSpecifications {
|
||||
return (targetRoot, query, cb) -> targetRoot.get(JpaTarget_.updateStatus).in(updateStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link Target}s by "not equal to
|
||||
* given {@link TargetUpdateStatus}".
|
||||
*
|
||||
* @param updateStatus
|
||||
* to be filtered on
|
||||
*
|
||||
* @return the {@link Target} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaTarget> notEqualToTargetUpdateStatus(final TargetUpdateStatus updateStatus) {
|
||||
return (targetRoot, query, cb) -> cb.not(cb.equal(targetRoot.get(JpaTarget_.updateStatus), updateStatus));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link Target}s that are overdue. A
|
||||
* target is overdue if it did not respond during the configured
|
||||
|
||||
@@ -428,6 +428,38 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
return action;
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Simple offline deployment of a distribution set to a list of targets. Verifies that offline assigment "
|
||||
+ "is correctly executed for targets that do not have a running update already. Those are ignored.")
|
||||
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 20),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 20), @Expect(type = ActionCreatedEvent.class, count = 20),
|
||||
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
|
||||
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 10) })
|
||||
public void assignedDistributionSet() {
|
||||
final List<String> controllerIds = testdataFactory.createTargets(10).stream().map(Target::getControllerId)
|
||||
.collect(Collectors.toList());
|
||||
final List<Target> onlineAssignedTargets = testdataFactory.createTargets(10, "2");
|
||||
controllerIds.addAll(onlineAssignedTargets.stream().map(Target::getControllerId).collect(Collectors.toList()));
|
||||
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||
assignDistributionSet(testdataFactory.createDistributionSet("2"), onlineAssignedTargets);
|
||||
|
||||
final long current = System.currentTimeMillis();
|
||||
final List<Target> targets = deploymentManagement.offlineAssignedDistributionSet(ds.getId(), controllerIds)
|
||||
.getAssignedEntity();
|
||||
assertThat(actionRepository.count()).isEqualTo(20);
|
||||
|
||||
assertThat(targetManagement.findTargetByInstalledDistributionSet(ds.getId(), PAGE).getContent())
|
||||
.containsAll(targets).hasSize(10)
|
||||
.containsAll(targetManagement.findTargetByAssignedDistributionSet(ds.getId(), PAGE))
|
||||
.as("InstallationDate set").allMatch(target -> target.getInstallationDate() >= current)
|
||||
.as("TargetUpdateStatus IN_SYNC")
|
||||
.allMatch(target -> TargetUpdateStatus.IN_SYNC.equals(target.getUpdateStatus()))
|
||||
.as("InstallationDate equal to LastModifiedAt")
|
||||
.allMatch(target -> target.getLastModifiedAt().equals(target.getInstallationDate()));
|
||||
}
|
||||
|
||||
/**
|
||||
* test a simple deployment by calling the
|
||||
* {@link TargetRepository#assignDistributionSet(DistributionSet, Iterable)}
|
||||
|
||||
Reference in New Issue
Block a user