Introduce basic functionality for invalidation of distributionsets (#1179)
* Basic DS invalidation functionality Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Add checks for valid/complete DS Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Stop rollouts + auto assignments when invalidating a DS Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Add methods to count AAs + rollouts for invalidation Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Small refactoring for DS management Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Add invalidation functionality to REST API Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Fix update stopped rollouts status Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Add various tests Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Introduce countActionsForInvalidation Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Fix event tests with incomplete DS Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Add H2 migration script Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Fix action count method Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Fix REST documentation tests Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Change flyway version number Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Add lock for DS invalidation + adapt tests Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Move concurrency test to own class Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Handle possible InterruptedException Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Fix concurrency test Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Use one transaction for all invalidations Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Add invalidate endpoint to REST docu Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Execute invalidation in transaction when actions are cancelled Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Check that distribution set is valid when editing/creating metadata Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Remove all changes in UI Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Add DB migration files for all databases Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Implement review findings Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Move DS invalidation to own class to check permissions for single steps Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Move invalidation count methods to management classes Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com> * Fix failing tests Signed-off-by: Sebastian Firsching <sebastian.firsching@bosch-si.com>
This commit is contained in:
committed by
GitHub
parent
b25e118e6c
commit
825cb64448
@@ -180,6 +180,11 @@ public enum SpServerError {
|
||||
SP_DS_INCOMPLETE("hawkbit.server.error.distributionset.incomplete",
|
||||
"Distribution set is assigned to a target that is incomplete (i.e. mandatory modules are missing)"),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
SP_DS_INVALID("hawkbit.server.error.distributionset.invalid", "Invalid distribution set is assigned to a target"),
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@@ -251,13 +256,6 @@ public enum SpServerError {
|
||||
SP_AUTO_ASSIGN_ACTION_TYPE_INVALID("hawkbit.server.error.repo.invalidAutoAssignActionType",
|
||||
"The given action type for auto-assignment is invalid: allowed values are ['forced', 'soft', 'downloadonly']"),
|
||||
|
||||
/**
|
||||
* Error message informing that the distribution set for auto-assignment is
|
||||
* invalid.
|
||||
*/
|
||||
SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID("hawkbit.server.error.repo.invalidAutoAssignDistributionSet",
|
||||
"The given distribution set for auto-assignment is invalid: it is either incomplete (i.e. mandatory modules are missing) or soft deleted"),
|
||||
|
||||
/**
|
||||
* Error message informing the user that the requested tenant configuration
|
||||
* change is not allowed.
|
||||
@@ -277,7 +275,9 @@ public enum SpServerError {
|
||||
SP_NO_WEIGHT_PROVIDED_IN_MULTIASSIGNMENT_MODE("hawkbit.server.error.noWeightProvidedInMultiAssignmentMode",
|
||||
"The requested operation requires a weight to be specified when multi assignments is enabled."),
|
||||
|
||||
SP_TARGET_TYPE_IN_USE("hawkbit.server.error.target.type.used", "Target type is still in use by a target.");
|
||||
SP_TARGET_TYPE_IN_USE("hawkbit.server.error.target.type.used", "Target type is still in use by a target."),
|
||||
|
||||
SP_STOP_ROLLOUT_FAILED("hawkbit.server.error.stopRolloutFailed", "Stopping the rollout failed");
|
||||
|
||||
private final String key;
|
||||
private final String message;
|
||||
|
||||
@@ -20,11 +20,11 @@ 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.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
@@ -34,6 +34,7 @@ import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
||||
import org.eclipse.hawkbit.repository.model.DeploymentRequestBuilder;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
@@ -76,7 +77,8 @@ public interface DeploymentManagement {
|
||||
*
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY_AND_UPDATE_TARGET)
|
||||
List<DistributionSetAssignmentResult> assignDistributionSets(@Valid @NotEmpty List<DeploymentRequest> deploymentRequests);
|
||||
List<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
@Valid @NotEmpty List<DeploymentRequest> deploymentRequests);
|
||||
|
||||
/**
|
||||
* Assigns {@link DistributionSet}s to {@link Target}s according to the
|
||||
@@ -228,6 +230,29 @@ public interface DeploymentManagement {
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
long countActionsByTarget(@NotEmpty String controllerId);
|
||||
|
||||
/**
|
||||
* Counts all active {@link Action}s referring to the given DistributionSet.
|
||||
*
|
||||
* @param distributionSet
|
||||
* DistributionSet to count the {@link Action}s from
|
||||
* @return the count of actions referring to the given distributionSet
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
long countActionsByDistributionSetIdAndActiveIsTrue(Long distributionSet);
|
||||
|
||||
/**
|
||||
* Counts all active {@link Action}s referring to the given DistributionSet
|
||||
* that are not in a given state.
|
||||
*
|
||||
* @param distributionSet
|
||||
* DistributionSet to count the {@link Action}s from
|
||||
* @param status
|
||||
* the state the actions should not have
|
||||
* @return the count of actions referring to the given distributionSet
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
long countActionsByDistributionSetIdAndActiveIsTrueAndStatusIsNot(Long distributionSet, Status status);
|
||||
|
||||
/**
|
||||
* Get the {@link Action} entity for given actionId.
|
||||
*
|
||||
@@ -506,8 +531,8 @@ public interface DeploymentManagement {
|
||||
int deleteActionsByStatusAndLastModifiedBefore(@NotNull Set<Action.Status> status, long lastModified);
|
||||
|
||||
/**
|
||||
* Checks if there is an action for the device with the given controller ID that
|
||||
* is in the {@link Action.Status#CANCELING} state.
|
||||
* Checks if there is an action for the device with the given controller ID
|
||||
* that is in the {@link Action.Status#CANCELING} state.
|
||||
*
|
||||
* @param controllerId
|
||||
* of target
|
||||
@@ -516,4 +541,16 @@ public interface DeploymentManagement {
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
boolean hasPendingCancellations(@NotEmpty String controllerId);
|
||||
|
||||
/**
|
||||
* Cancels all actions that refer to a given distribution set. This method
|
||||
* is called when a distribution set is invalidated.
|
||||
*
|
||||
* @param cancelationType
|
||||
* defines if a force or soft cancel is executed
|
||||
* @param set
|
||||
* the distribution set for that the actions should be canceled
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
void cancelActionsForDistributionSet(final CancelationType cancelationType, final DistributionSet set);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO 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;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidationCount;
|
||||
|
||||
/**
|
||||
* A DistributionSetInvalidationManagement service provides operations to
|
||||
* invalidate {@link DistributionSet}s.
|
||||
*
|
||||
*/
|
||||
public interface DistributionSetInvalidationManagement {
|
||||
|
||||
/**
|
||||
* Invalidates a given {@link DistributionSet}. The invalidation always
|
||||
* cancels all auto assignments referring this {@link DistributionSet} and
|
||||
* can not be undone. Optionally, all rollouts and actions referring this
|
||||
* {@link DistributionSet} can be canceled.
|
||||
*
|
||||
* @param distributionSetInvalidation
|
||||
* defines the {@link DistributionSet} and options what should be
|
||||
* canceled
|
||||
*/
|
||||
void invalidateDistributionSet(final DistributionSetInvalidation distributionSetInvalidation);
|
||||
|
||||
/**
|
||||
* Counts all entities for a list of {@link DistributionSet}s that will be
|
||||
* canceled when invalidation is called for those {@link DistributionSet}s.
|
||||
*
|
||||
*
|
||||
* @param distributionSetInvalidation
|
||||
* defines the {@link DistributionSet} and options what should be
|
||||
* canceled
|
||||
* @return The {@link DistributionSetInvalidationCount} object that holds
|
||||
* information about the count of affected rollouts,
|
||||
* auto-assignments and actions
|
||||
*/
|
||||
DistributionSetInvalidationCount countEntitiesForInvalidation(
|
||||
final DistributionSetInvalidation distributionSetInvalidation);
|
||||
|
||||
}
|
||||
@@ -18,10 +18,12 @@ import javax.validation.constraints.NotNull;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.builder.DistributionSetCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.DistributionSetUpdate;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
|
||||
@@ -171,6 +173,60 @@ public interface DistributionSetManagement
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
Optional<DistributionSet> getByNameAndVersion(@NotEmpty String distributionName, @NotEmpty String version);
|
||||
|
||||
/**
|
||||
* Find distribution set by id and throw an exception if it is deleted,
|
||||
* incomplete or invalidated.
|
||||
*
|
||||
* @param id
|
||||
* id of {@link DistributionSet}
|
||||
*
|
||||
* @return the found valid {@link DistributionSet}
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* if distribution set with given ID does not exist
|
||||
*
|
||||
* @throws InvalidDistributionSetException
|
||||
* if distribution set with given ID is invalidated
|
||||
*
|
||||
* @throws IncompleteDistributionSetException
|
||||
* if distribution set with given ID is incomplete
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
DistributionSet getValidAndComplete(long id);
|
||||
|
||||
/**
|
||||
* Find distribution set by id and throw an exception if it is deleted or
|
||||
* invalidated.
|
||||
*
|
||||
* @param id
|
||||
* id of {@link DistributionSet}
|
||||
*
|
||||
* @return the found valid {@link DistributionSet}
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* if distribution set with given ID does not exist
|
||||
*
|
||||
* @throws InvalidDistributionSetException
|
||||
* if distribution set with given ID is invalidated
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
DistributionSet getValid(long id);
|
||||
|
||||
/**
|
||||
* Find distribution set by id and throw an exception if it is (soft)
|
||||
* deleted.
|
||||
*
|
||||
* @param id
|
||||
* id of {@link DistributionSet}
|
||||
*
|
||||
* @return the found valid {@link DistributionSet}
|
||||
*
|
||||
* @throws EntityNotFoundException
|
||||
* if distribution set with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
DistributionSet getOrElseThrowException(long id);
|
||||
|
||||
/**
|
||||
* Finds all meta data by the given distribution set id.
|
||||
*
|
||||
@@ -338,8 +394,8 @@ public interface DistributionSetManagement
|
||||
/**
|
||||
* Toggles {@link DistributionSetTag} assignment to given
|
||||
* {@link DistributionSet}s by means that if some (or all) of the targets in
|
||||
* the list have the {@link Tag} not yet assigned, they will be. Only if all of
|
||||
* theme have the tag already assigned they will be removed instead.
|
||||
* the list have the {@link Tag} not yet assigned, they will be. Only if all
|
||||
* of theme have the tag already assigned they will be removed instead.
|
||||
*
|
||||
* @param setIds
|
||||
* to toggle for
|
||||
@@ -421,4 +477,13 @@ public interface DistributionSetManagement
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_REPOSITORY)
|
||||
long countByTypeId(long typeId);
|
||||
|
||||
/**
|
||||
* Sets the specified {@link DistributionSet} as invalidated.
|
||||
*
|
||||
* @param set
|
||||
* the ID of the {@link DistributionSet} to be set to invalid
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_REPOSITORY)
|
||||
void invalidate(DistributionSet set);
|
||||
|
||||
}
|
||||
|
||||
@@ -59,6 +59,12 @@ public class RepositoryProperties {
|
||||
*/
|
||||
private int actionWeightIfAbsent = 1000;
|
||||
|
||||
/**
|
||||
* Defines a timeout for the lock during invalidation of distribution sets
|
||||
* (in seconds).
|
||||
*/
|
||||
private long dsInvalidationLockTimeout = 5;
|
||||
|
||||
public boolean isEagerPollPersistence() {
|
||||
return eagerPollPersistence;
|
||||
}
|
||||
@@ -107,4 +113,12 @@ public class RepositoryProperties {
|
||||
this.actionWeightIfAbsent = actionWeightIfAbsent;
|
||||
}
|
||||
|
||||
public long getDsInvalidationLockTimeout() {
|
||||
return dsInvalidationLockTimeout;
|
||||
}
|
||||
|
||||
public void setDsInvalidationLockTimeout(final long dsInvalidationLockTimeout) {
|
||||
this.dsInvalidationLockTimeout = dsInvalidationLockTimeout;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutUpdate;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
|
||||
@@ -95,6 +95,17 @@ public interface RolloutManagement {
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
long countByFilters(@NotEmpty String searchText);
|
||||
|
||||
/**
|
||||
* Counts all {@link Rollout}s for a specific {@link DistributionSet} that
|
||||
* are stoppable
|
||||
*
|
||||
* @param setId
|
||||
* the distribution set
|
||||
* @return the count
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_READ)
|
||||
long countByDistributionSetIdAndRolloutIsStoppable(long setId);
|
||||
|
||||
/**
|
||||
* Persists a new rollout entity. The filter within the
|
||||
* {@link Rollout#getTargetFilterQuery()} is used to retrieve the targets
|
||||
@@ -438,4 +449,16 @@ public interface RolloutManagement {
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_DELETE)
|
||||
void delete(long rolloutId);
|
||||
|
||||
/**
|
||||
* Cancels all rollouts that refer to the given {@link DistributionSet}.
|
||||
* This is called when a distribution set is invalidated and the cancel
|
||||
* rollouts option is activated.
|
||||
*
|
||||
* @param set
|
||||
* the {@link DistributionSet} for that the rollouts should be
|
||||
* canceled
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_ROLLOUT_MANAGEMENT_UPDATE)
|
||||
void cancelRolloutsForDistributionSet(DistributionSet set);
|
||||
|
||||
}
|
||||
|
||||
@@ -18,10 +18,11 @@ import org.eclipse.hawkbit.im.authentication.SpPermission.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryUpdate;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
@@ -105,6 +106,17 @@ public interface TargetFilterQueryManagement {
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
long count();
|
||||
|
||||
/**
|
||||
* Counts all target filters that have a given auto assign distribution set
|
||||
* assigned.
|
||||
*
|
||||
* @param autoAssignDistributionSetId
|
||||
* the id of the distribution set
|
||||
* @return the count
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_READ_TARGET)
|
||||
long countByAutoAssignDistributionSetId(long autoAssignDistributionSetId);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link TargetFilterQuery}s which match the given name
|
||||
* filter.
|
||||
@@ -236,11 +248,25 @@ public interface TargetFilterQueryManagement {
|
||||
* if the provided auto-assign {@link ActionType} is not valid
|
||||
* (neither FORCED, nor SOFT)
|
||||
*
|
||||
* @throws InvalidAutoAssignDistributionSetException
|
||||
* if the provided auto-assign {@link DistributionSet} is not
|
||||
* valid (incomplete or soft deleted)
|
||||
* @throws IncompleteDistributionSetException
|
||||
* if the provided auto-assign {@link DistributionSet} is
|
||||
* incomplete
|
||||
*
|
||||
* @throws InvalidDistributionSetException
|
||||
* if the provided auto-assign {@link DistributionSet} is
|
||||
* invalidated
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
TargetFilterQuery updateAutoAssignDS(
|
||||
@NotNull @Valid AutoAssignDistributionSetUpdate autoAssignDistributionSetUpdate);
|
||||
|
||||
/**
|
||||
* Removes the given {@link DistributionSet} from all auto assignments.
|
||||
*
|
||||
* @param setId
|
||||
* the {@link DistributionSet} to be removed from auto
|
||||
* assignments.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_UPDATE_TARGET)
|
||||
void cancelAutoAssignmentForDistributionSet(long setId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO 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.event.remote;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
|
||||
/**
|
||||
*
|
||||
* Event that is published when a rollout is stopped due to invalidation of a
|
||||
* {@link DistributionSet}.
|
||||
*/
|
||||
public class RolloutStoppedEvent extends RemoteTenantAwareEvent {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Collection<Long> rolloutGroupIds;
|
||||
private long rolloutId;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public RolloutStoppedEvent() {
|
||||
// for serialization libs like jackson
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for json serialization.
|
||||
*
|
||||
* @param tenant
|
||||
* the tenant
|
||||
* @param entityId
|
||||
* the entity id
|
||||
* @param entityClass
|
||||
* the entity class
|
||||
* @param applicationId
|
||||
* the origin application id
|
||||
*/
|
||||
public RolloutStoppedEvent(final String tenant, final String applicationId, final long rolloutId,
|
||||
final Collection<Long> rolloutGroupIds) {
|
||||
super(rolloutId, tenant, applicationId);
|
||||
this.rolloutId = rolloutId;
|
||||
this.rolloutGroupIds = rolloutGroupIds;
|
||||
}
|
||||
|
||||
public Collection<Long> getRolloutGroupIds() {
|
||||
return rolloutGroupIds;
|
||||
}
|
||||
|
||||
public void setRolloutGroupIds(final Collection<Long> rolloutGroupIds) {
|
||||
this.rolloutGroupIds = rolloutGroupIds;
|
||||
}
|
||||
|
||||
public long getRolloutId() {
|
||||
return rolloutId;
|
||||
}
|
||||
|
||||
public void setRolloutId(final long rolloutId) {
|
||||
this.rolloutId = rolloutId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2019 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.exception;
|
||||
|
||||
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
|
||||
/**
|
||||
* Thrown if a distribution set for auto-assignment is incomplete (i.e.
|
||||
* mandatory modules are missing) or soft deleted.
|
||||
*/
|
||||
public class InvalidAutoAssignDistributionSetException extends AbstractServerRtException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final SpServerError THIS_ERROR = SpServerError.SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public InvalidAutoAssignDistributionSetException() {
|
||||
super(THIS_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO 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.exception;
|
||||
|
||||
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
|
||||
public class InvalidDistributionSetException extends AbstractServerRtException {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a new InvalidDistributionSetException with
|
||||
* {@link SpServerError#SP_DS_INVALID} error.
|
||||
*/
|
||||
public InvalidDistributionSetException() {
|
||||
super(SpServerError.SP_DS_INVALID);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cause
|
||||
* for the exception
|
||||
*/
|
||||
public InvalidDistributionSetException(final Throwable cause) {
|
||||
super(SpServerError.SP_DS_INVALID, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param message
|
||||
* of the error
|
||||
*/
|
||||
public InvalidDistributionSetException(final String message) {
|
||||
super(message, SpServerError.SP_DS_INVALID);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO 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.exception;
|
||||
|
||||
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
|
||||
/**
|
||||
* {@link StopRolloutException} is thrown when an error occurs while stopping
|
||||
* the rollout (due to an invalidation of distribution set). This could be
|
||||
* caused by a long ongoing creation of a rollout.
|
||||
*/
|
||||
public class StopRolloutException extends AbstractServerRtException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a new StopRolloutException with
|
||||
* {@link SpServerError#SP_STOP_ROLLOUT_FAILED} error.
|
||||
*/
|
||||
public StopRolloutException() {
|
||||
super(SpServerError.SP_STOP_ROLLOUT_FAILED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new StopRolloutException with
|
||||
* {@link SpServerError#SP_STOP_ROLLOUT_FAILED} error.
|
||||
*
|
||||
* @param cause
|
||||
* for the exception
|
||||
*/
|
||||
public StopRolloutException(final Throwable cause) {
|
||||
super(SpServerError.SP_STOP_ROLLOUT_FAILED, cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new StopRolloutException with
|
||||
* {@link SpServerError#SP_STOP_ROLLOUT_FAILED} error.
|
||||
*
|
||||
* @param message
|
||||
* of the error
|
||||
*/
|
||||
public StopRolloutException(final String message) {
|
||||
super(message, SpServerError.SP_STOP_ROLLOUT_FAILED);
|
||||
}
|
||||
}
|
||||
@@ -71,4 +71,10 @@ public interface DistributionSet extends NamedVersionedEntity {
|
||||
*/
|
||||
boolean isComplete();
|
||||
|
||||
/**
|
||||
* @return <code>false</code> if this {@link DistributionSet} is
|
||||
* invalidated.
|
||||
*/
|
||||
boolean isValid();
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ public final class DistributionSetFilter {
|
||||
public static class DistributionSetFilterBuilder {
|
||||
private Boolean isDeleted;
|
||||
private Boolean isComplete;
|
||||
private Boolean isValid;
|
||||
private DistributionSetType type;
|
||||
private String searchText;
|
||||
private String filterString;
|
||||
@@ -57,6 +58,11 @@ public final class DistributionSetFilter {
|
||||
return this;
|
||||
}
|
||||
|
||||
public DistributionSetFilterBuilder setIsValid(final Boolean isValid) {
|
||||
this.isValid = isValid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DistributionSetFilterBuilder setSearchText(final String searchText) {
|
||||
this.searchText = searchText;
|
||||
return this;
|
||||
@@ -86,6 +92,7 @@ public final class DistributionSetFilter {
|
||||
|
||||
private final Boolean isDeleted;
|
||||
private final Boolean isComplete;
|
||||
private final Boolean isValid;
|
||||
private final DistributionSetType type;
|
||||
private final String searchText;
|
||||
private final String filterString;
|
||||
@@ -104,6 +111,7 @@ public final class DistributionSetFilter {
|
||||
public DistributionSetFilter(final DistributionSetFilterBuilder builder) {
|
||||
this.isDeleted = builder.isDeleted;
|
||||
this.isComplete = builder.isComplete;
|
||||
this.isValid = builder.isValid;
|
||||
this.type = builder.type;
|
||||
this.searchText = builder.searchText;
|
||||
this.filterString = builder.filterString;
|
||||
@@ -129,6 +137,10 @@ public final class DistributionSetFilter {
|
||||
return isDeleted;
|
||||
}
|
||||
|
||||
public Boolean getIsValid() {
|
||||
return isValid;
|
||||
}
|
||||
|
||||
public String getSearchText() {
|
||||
return searchText;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO 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.model;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Holds the information about the invalidation of a distribution set
|
||||
*/
|
||||
public class DistributionSetInvalidation {
|
||||
|
||||
private Collection<Long> distributionSetIds;
|
||||
private CancelationType cancelationType;
|
||||
private boolean cancelRollouts;
|
||||
|
||||
/**
|
||||
* Parametric constructor
|
||||
*
|
||||
* @param distributionSetIds
|
||||
* defines which distribution sets should be canceled
|
||||
* @param cancelationType
|
||||
* defines if actions should be canceled
|
||||
* @param cancelRollouts
|
||||
* defines if rollouts should be canceled
|
||||
*/
|
||||
public DistributionSetInvalidation(final Collection<Long> distributionSetIds, final CancelationType cancelationType,
|
||||
final boolean cancelRollouts) {
|
||||
this.distributionSetIds = distributionSetIds;
|
||||
this.cancelationType = cancelationType;
|
||||
this.cancelRollouts = cancelRollouts;
|
||||
}
|
||||
|
||||
public Collection<Long> getDistributionSetIds() {
|
||||
return distributionSetIds;
|
||||
}
|
||||
|
||||
public void setDistributionSetIds(final Collection<Long> distributionSetIds) {
|
||||
this.distributionSetIds = distributionSetIds;
|
||||
}
|
||||
|
||||
public CancelationType getCancelationType() {
|
||||
return cancelationType;
|
||||
}
|
||||
|
||||
public void setCancelationType(final CancelationType cancelationType) {
|
||||
this.cancelationType = cancelationType;
|
||||
}
|
||||
|
||||
public boolean isCancelRollouts() {
|
||||
return cancelRollouts;
|
||||
}
|
||||
|
||||
public void setCancelRollouts(final boolean cancelRollouts) {
|
||||
this.cancelRollouts = cancelRollouts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if and how actions should be canceled when invalidating a
|
||||
* distribution set
|
||||
*/
|
||||
public enum CancelationType {
|
||||
FORCE, SOFT, NONE;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO 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.model;
|
||||
|
||||
/**
|
||||
* object that holds information about the count of affected rollouts,
|
||||
* auto-assignments and actions, when a list of distribution sets gets
|
||||
* invalidated
|
||||
*/
|
||||
public class DistributionSetInvalidationCount {
|
||||
|
||||
private final long rolloutsCount;
|
||||
private final long autoAssignmentCount;
|
||||
private final long actionCount;
|
||||
|
||||
public DistributionSetInvalidationCount(final long rolloutsCount, final long autoAssignmentCount,
|
||||
final long actionCount) {
|
||||
this.rolloutsCount = rolloutsCount;
|
||||
this.autoAssignmentCount = autoAssignmentCount;
|
||||
this.actionCount = actionCount;
|
||||
}
|
||||
|
||||
public long getRolloutsCount() {
|
||||
return rolloutsCount;
|
||||
}
|
||||
|
||||
public long getAutoAssignmentCount() {
|
||||
return autoAssignmentCount;
|
||||
}
|
||||
|
||||
public long getActionCount() {
|
||||
return actionCount;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -149,6 +149,11 @@ public interface Rollout extends NamedEntity {
|
||||
*/
|
||||
STOPPED,
|
||||
|
||||
/**
|
||||
* Rollout is going to be stopped.
|
||||
*/
|
||||
STOPPING,
|
||||
|
||||
/**
|
||||
* Rollout is running.
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RolloutGroupDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RolloutStoppedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.SoftwareModuleTypeDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||
@@ -146,6 +147,9 @@ public class EventType {
|
||||
TYPES.put(40, TenantConfigurationCreatedEvent.class);
|
||||
TYPES.put(41, TenantConfigurationUpdatedEvent.class);
|
||||
TYPES.put(42, TenantConfigurationDeletedEvent.class);
|
||||
|
||||
// rollout stopped due to invalidated distribution set
|
||||
TYPES.put(43, RolloutStoppedEvent.class);
|
||||
}
|
||||
|
||||
private int value;
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.eclipse.hawkbit.cache.TenancyCacheManager;
|
||||
import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RolloutGroupDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RolloutStoppedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.AbstractActionEvent;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
@@ -223,6 +224,18 @@ public class RolloutStatusCache {
|
||||
cache.evict(event.getEntityId());
|
||||
}
|
||||
|
||||
@EventListener(classes = RolloutStoppedEvent.class)
|
||||
void invalidateCachedTotalTargetCountOnRolloutStopped(final RolloutStoppedEvent event) {
|
||||
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> cacheManager.getCache(CACHE_RO_NAME));
|
||||
cache.evict(event.getRolloutId());
|
||||
|
||||
for (final long groupId : event.getRolloutGroupIds()) {
|
||||
final Cache groupCache = tenantAware.runAsTenant(event.getTenant(),
|
||||
() -> cacheManager.getCache(CACHE_GR_NAME));
|
||||
groupCache.evict(groupId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evicts all caches for a given tenant. All caches under a certain tenant
|
||||
* gets evicted.
|
||||
|
||||
@@ -64,6 +64,28 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
|
||||
*/
|
||||
Page<Action> findByDistributionSetId(Pageable pageable, Long dsId);
|
||||
|
||||
/**
|
||||
* Retrieves all active {@link Action}s which are referring the given
|
||||
* {@link DistributionSet}.
|
||||
*
|
||||
* @param set
|
||||
* the {@link DistributionSet} on which will be filtered
|
||||
* @return the found {@link Action}s
|
||||
*/
|
||||
List<Action> findByDistributionSetAndActiveIsTrue(DistributionSet set);
|
||||
|
||||
/**
|
||||
* Retrieves all active {@link Action}s which are referring the given
|
||||
* {@link DistributionSet} and are not in the given state
|
||||
*
|
||||
* @param set
|
||||
* the {@link DistributionSet} on which will be filtered
|
||||
* @param status
|
||||
* the state the actions should not have
|
||||
* @return the found {@link Action}s
|
||||
*/
|
||||
List<Action> findByDistributionSetAndActiveIsTrueAndStatusIsNot(DistributionSet set, Status status);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Action}s which are referring the given
|
||||
* {@link Target}.
|
||||
@@ -264,7 +286,6 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
|
||||
List<Action> findByExternalRefInAndActive(@Param("externalRefs") List<String> externalRefs,
|
||||
@Param("active") boolean active);
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves an {@link Action} that matches the queried externalRef.
|
||||
*
|
||||
@@ -351,6 +372,27 @@ public interface ActionRepository extends BaseEntityRepository<JpaAction, Long>,
|
||||
*/
|
||||
Long countByDistributionSetId(Long distributionSet);
|
||||
|
||||
/**
|
||||
* Counts all active {@link Action}s referring to the given DistributionSet.
|
||||
*
|
||||
* @param distributionSet
|
||||
* DistributionSet to count the {@link Action}s from
|
||||
* @return the count of actions referring to the given distributionSet
|
||||
*/
|
||||
Long countByDistributionSetIdAndActiveIsTrue(Long distributionSet);
|
||||
|
||||
/**
|
||||
* Counts all active {@link Action}s referring to the given DistributionSet
|
||||
* that are not in a given state.
|
||||
*
|
||||
* @param distributionSet
|
||||
* DistributionSet to count the {@link Action}s from
|
||||
* @param status
|
||||
* the state the actions should not have
|
||||
* @return the count of actions referring to the given distributionSet
|
||||
*/
|
||||
Long countByDistributionSetIdAndActiveIsTrueAndStatusIsNot(Long distributionSet, Status status);
|
||||
|
||||
/**
|
||||
* Counts all actions referring to a given rollout and rolloutgroup which
|
||||
* are currently not in the given status. An in-clause statement does not
|
||||
|
||||
@@ -35,6 +35,7 @@ import javax.persistence.criteria.Root;
|
||||
|
||||
import org.eclipse.hawkbit.repository.ActionFields;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
@@ -43,7 +44,6 @@ import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEv
|
||||
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||
@@ -67,6 +67,7 @@ import org.eclipse.hawkbit.repository.model.ActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
@@ -130,6 +131,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
|
||||
private final EntityManager entityManager;
|
||||
private final DistributionSetManagement distributionSetManagement;
|
||||
private final DistributionSetRepository distributionSetRepository;
|
||||
private final TargetRepository targetRepository;
|
||||
private final ActionStatusRepository actionStatusRepository;
|
||||
@@ -146,6 +148,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
private final RetryTemplate retryTemplate;
|
||||
|
||||
protected JpaDeploymentManagement(final EntityManager entityManager, final ActionRepository actionRepository,
|
||||
final DistributionSetManagement distributionSetManagement,
|
||||
final DistributionSetRepository distributionSetRepository, final TargetRepository targetRepository,
|
||||
final ActionStatusRepository actionStatusRepository, final AuditorAware<String> auditorProvider,
|
||||
final EventPublisherHolder eventPublisherHolder, final AfterTransactionCommitExecutor afterCommit,
|
||||
@@ -156,6 +159,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
super(actionRepository, repositoryProperties);
|
||||
this.entityManager = entityManager;
|
||||
this.distributionSetRepository = distributionSetRepository;
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.targetRepository = targetRepository;
|
||||
this.actionStatusRepository = actionStatusRepository;
|
||||
this.auditorProvider = auditorProvider;
|
||||
@@ -285,7 +289,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy) {
|
||||
|
||||
final JpaDistributionSet distributionSetEntity = getAndValidateDsById(dsID);
|
||||
final JpaDistributionSet distributionSetEntity = (JpaDistributionSet) distributionSetManagement
|
||||
.getValidAndComplete(dsID);
|
||||
final List<String> providedTargetIds = targetsWithActionType.stream().map(TargetWithActionType::getControllerId)
|
||||
.distinct().collect(Collectors.toList());
|
||||
|
||||
@@ -363,18 +368,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
return new DistributionSetAssignmentResult(distributionSet, alreadyAssignedTargetsCount, assignedActions);
|
||||
}
|
||||
|
||||
private JpaDistributionSet getAndValidateDsById(final Long dsID) {
|
||||
final JpaDistributionSet distributionSet = distributionSetRepository.findById(dsID)
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, dsID));
|
||||
|
||||
if (!distributionSet.isComplete()) {
|
||||
throw new IncompleteDistributionSetException("Distribution set of type "
|
||||
+ distributionSet.getType().getKey() + " is incomplete: " + distributionSet.getId());
|
||||
}
|
||||
|
||||
return distributionSet;
|
||||
}
|
||||
|
||||
private void checkQuotaForAssignment(final Collection<DeploymentRequest> deploymentRequests) {
|
||||
if (!deploymentRequests.isEmpty()) {
|
||||
enforceMaxAssignmentsPerRequest(deploymentRequests.size());
|
||||
@@ -656,8 +649,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
|
||||
private Specification<JpaAction> createSpecificationFor(final String controllerId, final String rsqlParam) {
|
||||
final Specification<JpaAction> spec = RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class, virtualPropertyReplacer,
|
||||
database);
|
||||
final Specification<JpaAction> spec = RSQLUtility.buildRsqlSpecification(rsqlParam, ActionFields.class,
|
||||
virtualPropertyReplacer, database);
|
||||
return (root, query, cb) -> cb.and(spec.toPredicate(root, query, cb),
|
||||
cb.equal(root.get(JpaAction_.target).get(JpaTarget_.controllerId), controllerId));
|
||||
}
|
||||
@@ -779,6 +772,17 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
return actionRepository.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countActionsByDistributionSetIdAndActiveIsTrue(final Long distributionSet) {
|
||||
return actionRepository.countByDistributionSetIdAndActiveIsTrue(distributionSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countActionsByDistributionSetIdAndActiveIsTrueAndStatusIsNot(final Long distributionSet,
|
||||
final Status status) {
|
||||
return actionRepository.countByDistributionSetIdAndActiveIsTrueAndStatusIsNot(distributionSet, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Action> findActionsByDistributionSet(final Pageable pageable, final long dsId) {
|
||||
throwExceptionIfDistributionSetDoesNotExist(dsId);
|
||||
@@ -832,7 +836,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPendingCancellations(String controllerId) {
|
||||
public boolean hasPendingCancellations(final String controllerId) {
|
||||
return actionRepository.existsByTargetControllerIdAndStatusAndActiveIsTrue(controllerId,
|
||||
Action.Status.CANCELING);
|
||||
}
|
||||
@@ -881,4 +885,21 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void cancelActionsForDistributionSet(final CancelationType cancelationType, final DistributionSet set) {
|
||||
actionRepository.findByDistributionSetAndActiveIsTrueAndStatusIsNot(set, Status.CANCELING).forEach(action -> {
|
||||
final JpaAction jpaAction = (JpaAction) action;
|
||||
cancelAction(jpaAction.getId());
|
||||
LOG.debug("Action {} canceled", jpaAction.getId());
|
||||
});
|
||||
if (cancelationType == CancelationType.FORCE) {
|
||||
actionRepository.findByDistributionSetAndActiveIsTrue(set).forEach(action -> {
|
||||
final JpaAction jpaAction = (JpaAction) action;
|
||||
forceQuitAction(jpaAction.getId());
|
||||
LOG.debug("Action {} force canceled", jpaAction.getId());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO 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.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetInvalidationManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.StopRolloutException;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidationCount;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.integration.support.locks.LockRegistry;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
/**
|
||||
* Jpa implementation for {@link DistributionSetInvalidationManagement}
|
||||
*
|
||||
*/
|
||||
public class JpaDistributionSetInvalidationManagement implements DistributionSetInvalidationManagement {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JpaDistributionSetInvalidationManagement.class);
|
||||
|
||||
private final DistributionSetManagement distributionSetManagement;
|
||||
private final RolloutManagement rolloutManagement;
|
||||
private final DeploymentManagement deploymentManagement;
|
||||
private final TargetFilterQueryManagement targetFilterQueryManagement;
|
||||
private final PlatformTransactionManager txManager;
|
||||
private final RepositoryProperties repositoryProperties;
|
||||
private final TenantAware tenantAware;
|
||||
private final LockRegistry lockRegistry;
|
||||
|
||||
protected JpaDistributionSetInvalidationManagement(final DistributionSetManagement distributionSetManagement,
|
||||
final RolloutManagement rolloutManagement, final DeploymentManagement deploymentManagement,
|
||||
final TargetFilterQueryManagement targetFilterQueryManagement, final PlatformTransactionManager txManager,
|
||||
final RepositoryProperties repositoryProperties, final TenantAware tenantAware,
|
||||
final LockRegistry lockRegistry) {
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.rolloutManagement = rolloutManagement;
|
||||
this.deploymentManagement = deploymentManagement;
|
||||
this.targetFilterQueryManagement = targetFilterQueryManagement;
|
||||
this.txManager = txManager;
|
||||
this.repositoryProperties = repositoryProperties;
|
||||
this.tenantAware = tenantAware;
|
||||
this.lockRegistry = lockRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateDistributionSet(final DistributionSetInvalidation distributionSetInvalidation) {
|
||||
LOG.debug("Invalidate distribution sets {}", distributionSetInvalidation.getDistributionSetIds());
|
||||
final String tenant = tenantAware.getCurrentTenant();
|
||||
|
||||
if (shouldRolloutsBeCanceled(distributionSetInvalidation.getCancelationType(),
|
||||
distributionSetInvalidation.isCancelRollouts())) {
|
||||
final String handlerId = JpaRolloutManagement.createRolloutLockKey(tenant);
|
||||
final Lock lock = lockRegistry.obtain(handlerId);
|
||||
try {
|
||||
if (!lock.tryLock(repositoryProperties.getDsInvalidationLockTimeout(), TimeUnit.SECONDS)) {
|
||||
throw new StopRolloutException("Timeout while trying to invalidate distribution sets");
|
||||
}
|
||||
try {
|
||||
invalidateDistributionSetsInTransaction(distributionSetInvalidation, tenant);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} catch (final InterruptedException e) {
|
||||
LOG.error("InterruptedException while invalidating distribution sets {}!",
|
||||
distributionSetInvalidation.getDistributionSetIds(), e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
} else {
|
||||
// no lock is needed as no rollout will be stopped
|
||||
invalidateDistributionSetsInTransaction(distributionSetInvalidation, tenant);
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidateDistributionSetsInTransaction(final DistributionSetInvalidation distributionSetInvalidation,
|
||||
final String tenant) {
|
||||
DeploymentHelper.runInNewTransaction(txManager, tenant + "-invalidateDS", status -> {
|
||||
distributionSetInvalidation.getDistributionSetIds().forEach(setId -> invalidateDistributionSet(setId,
|
||||
distributionSetInvalidation.getCancelationType(), distributionSetInvalidation.isCancelRollouts()));
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
private void invalidateDistributionSet(final long setId, final CancelationType cancelationType,
|
||||
final boolean cancelRollouts) {
|
||||
final DistributionSet set = distributionSetManagement.getValidAndComplete(setId);
|
||||
distributionSetManagement.invalidate(set);
|
||||
LOG.debug("Distribution set {} set to invalid", setId);
|
||||
|
||||
if (shouldRolloutsBeCanceled(cancelationType, cancelRollouts)) {
|
||||
rolloutManagement.cancelRolloutsForDistributionSet(set);
|
||||
}
|
||||
|
||||
if (cancelationType != CancelationType.NONE) {
|
||||
deploymentManagement.cancelActionsForDistributionSet(cancelationType, set);
|
||||
}
|
||||
|
||||
targetFilterQueryManagement.cancelAutoAssignmentForDistributionSet(setId);
|
||||
}
|
||||
|
||||
private static boolean shouldRolloutsBeCanceled(final CancelationType cancelationType,
|
||||
final boolean cancelRollouts) {
|
||||
return cancelationType != CancelationType.NONE || cancelRollouts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSetInvalidationCount countEntitiesForInvalidation(
|
||||
final DistributionSetInvalidation distributionSetInvalidation) {
|
||||
final Collection<Long> setIds = distributionSetInvalidation.getDistributionSetIds();
|
||||
final long rolloutsCount = shouldRolloutsBeCanceled(distributionSetInvalidation.getCancelationType(),
|
||||
distributionSetInvalidation.isCancelRollouts()) ? countRolloutsForInvalidation(setIds) : 0;
|
||||
final long autoAssignmentsCount = countAutoAssignmentsForInvalidation(setIds);
|
||||
final long actionsCount = countActionsForInvalidation(setIds, distributionSetInvalidation.getCancelationType());
|
||||
|
||||
return new DistributionSetInvalidationCount(rolloutsCount, autoAssignmentsCount, actionsCount);
|
||||
}
|
||||
|
||||
private long countRolloutsForInvalidation(final Collection<Long> setIds) {
|
||||
return setIds.stream().mapToLong(rolloutManagement::countByDistributionSetIdAndRolloutIsStoppable).sum();
|
||||
}
|
||||
|
||||
private long countAutoAssignmentsForInvalidation(final Collection<Long> setIds) {
|
||||
return setIds.stream().mapToLong(targetFilterQueryManagement::countByAutoAssignDistributionSetId).sum();
|
||||
}
|
||||
|
||||
private long countActionsForInvalidation(final Collection<Long> setIds, final CancelationType cancelationType) {
|
||||
long affectedActionsByDSInvalidation = 0;
|
||||
if (cancelationType == CancelationType.FORCE) {
|
||||
affectedActionsByDSInvalidation = countActionsForForcedInvalidation(setIds);
|
||||
} else if (cancelationType == CancelationType.SOFT) {
|
||||
affectedActionsByDSInvalidation = countActionsForSoftInvalidation(setIds);
|
||||
}
|
||||
return affectedActionsByDSInvalidation;
|
||||
}
|
||||
|
||||
private long countActionsForForcedInvalidation(final Collection<Long> setIds) {
|
||||
return setIds.stream().mapToLong(deploymentManagement::countActionsByDistributionSetIdAndActiveIsTrue).sum();
|
||||
}
|
||||
|
||||
private long countActionsForSoftInvalidation(final Collection<Long> setIds) {
|
||||
return setIds.stream().mapToLong(distributionSet -> deploymentManagement
|
||||
.countActionsByDistributionSetIdAndActiveIsTrueAndStatusIsNot(distributionSet, Status.CANCELING)).sum();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,6 +32,8 @@ import org.eclipse.hawkbit.repository.event.remote.DistributionSetDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaDistributionSetCreate;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||
@@ -213,7 +215,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
||||
public DistributionSet update(final DistributionSetUpdate u) {
|
||||
final GenericDistributionSetUpdate update = (GenericDistributionSetUpdate) u;
|
||||
|
||||
final JpaDistributionSet set = findDistributionSetAndThrowExceptionIfNotFound(update.getId());
|
||||
final JpaDistributionSet set = (JpaDistributionSet) getValid(update.getId());
|
||||
|
||||
update.getName().ifPresent(set::setName);
|
||||
update.getDescription().ifPresent(set::setDescription);
|
||||
@@ -228,11 +230,6 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
||||
return distributionSetRepository.save(set);
|
||||
}
|
||||
|
||||
private JpaDistributionSet findDistributionSetAndThrowExceptionIfNotFound(final Long setId) {
|
||||
return (JpaDistributionSet) get(setId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, setId));
|
||||
}
|
||||
|
||||
private JpaSoftwareModule findSoftwareModuleAndThrowExceptionIfNotFound(final Long moduleId) {
|
||||
return softwareModuleRepository.findById(moduleId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(SoftwareModule.class, moduleId));
|
||||
@@ -315,7 +312,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
||||
|
||||
assertDistributionSetIsNotAssignedToTargets(setId);
|
||||
|
||||
final JpaDistributionSet set = findDistributionSetAndThrowExceptionIfNotFound(setId);
|
||||
final JpaDistributionSet set = (JpaDistributionSet) getValid(setId);
|
||||
|
||||
assertSoftwareModuleQuota(setId, modules.size());
|
||||
|
||||
@@ -329,7 +326,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
||||
@Retryable(include = {
|
||||
ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX, backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public DistributionSet unassignSoftwareModule(final long setId, final long moduleId) {
|
||||
final JpaDistributionSet set = findDistributionSetAndThrowExceptionIfNotFound(setId);
|
||||
final JpaDistributionSet set = (JpaDistributionSet) getValid(setId);
|
||||
final JpaSoftwareModule module = findSoftwareModuleAndThrowExceptionIfNotFound(moduleId);
|
||||
|
||||
assertDistributionSetIsNotAssignedToTargets(setId);
|
||||
@@ -531,7 +528,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
||||
* of the DS to touch
|
||||
*/
|
||||
private JpaDistributionSet touch(final Long distId) {
|
||||
return touch(get(distId).orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, distId)));
|
||||
return touch(getValid(distId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -595,7 +592,7 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
||||
|
||||
private static List<Specification<JpaDistributionSet>> buildDistributionSetSpecifications(
|
||||
final DistributionSetFilter distributionSetFilter) {
|
||||
final List<Specification<JpaDistributionSet>> specList = Lists.newArrayListWithExpectedSize(8);
|
||||
final List<Specification<JpaDistributionSet>> specList = Lists.newArrayListWithExpectedSize(9);
|
||||
|
||||
Specification<JpaDistributionSet> spec;
|
||||
|
||||
@@ -609,6 +606,11 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
||||
specList.add(spec);
|
||||
}
|
||||
|
||||
if (distributionSetFilter.getIsValid() != null) {
|
||||
spec = DistributionSetSpecification.isValid(distributionSetFilter.getIsValid());
|
||||
specList.add(spec);
|
||||
}
|
||||
|
||||
if (distributionSetFilter.getType() != null) {
|
||||
spec = DistributionSetSpecification.byType(distributionSetFilter.getType());
|
||||
specList.add(spec);
|
||||
@@ -781,8 +783,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
||||
public Page<DistributionSet> findByRsqlAndTag(final Pageable pageable, final String rsqlParam, final long tagId) {
|
||||
throwEntityNotFoundExceptionIfDsTagDoesNotExist(tagId);
|
||||
|
||||
final Specification<JpaDistributionSet> spec = RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetFields.class,
|
||||
virtualPropertyReplacer, database);
|
||||
final Specification<JpaDistributionSet> spec = RSQLUtility.buildRsqlSpecification(rsqlParam,
|
||||
DistributionSetFields.class, virtualPropertyReplacer, database);
|
||||
|
||||
return convertDsPage(findByCriteriaAPI(pageable, Arrays.asList(spec, DistributionSetSpecification.hasTag(tagId),
|
||||
DistributionSetSpecification.isDeleted(false))), pageable);
|
||||
@@ -797,8 +799,8 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
||||
|
||||
@Override
|
||||
public Page<DistributionSet> findByRsql(final Pageable pageable, final String rsqlParam) {
|
||||
final Specification<JpaDistributionSet> spec = RSQLUtility.buildRsqlSpecification(rsqlParam, DistributionSetFields.class,
|
||||
virtualPropertyReplacer, database);
|
||||
final Specification<JpaDistributionSet> spec = RSQLUtility.buildRsqlSpecification(rsqlParam,
|
||||
DistributionSetFields.class, virtualPropertyReplacer, database);
|
||||
|
||||
return convertDsPage(
|
||||
findByCriteriaAPI(pageable, Arrays.asList(spec, DistributionSetSpecification.isDeleted(false))),
|
||||
@@ -815,4 +817,41 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
|
||||
return distributionSetRepository.existsById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSet getOrElseThrowException(final long id) {
|
||||
return get(id).orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSet getValidAndComplete(final long id) {
|
||||
final DistributionSet distributionSet = getValid(id);
|
||||
|
||||
if (!distributionSet.isComplete()) {
|
||||
throw new IncompleteDistributionSetException("Distribution set of type "
|
||||
+ distributionSet.getType().getKey() + " is incomplete: " + distributionSet.getId());
|
||||
}
|
||||
|
||||
return distributionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DistributionSet getValid(final long id) {
|
||||
final DistributionSet distributionSet = getOrElseThrowException(id);
|
||||
|
||||
if (!distributionSet.isValid()) {
|
||||
throw new InvalidDistributionSetException("Distribution set of type " + distributionSet.getType().getKey()
|
||||
+ " is invalid: " + distributionSet.getId());
|
||||
}
|
||||
|
||||
return distributionSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void invalidate(final DistributionSet set) {
|
||||
final JpaDistributionSet jpaSet = (JpaDistributionSet) set;
|
||||
jpaSet.invalidate();
|
||||
distributionSetRepository.save(jpaSet);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.eclipse.hawkbit.repository.RolloutHelper;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RolloutGroupDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RolloutStoppedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
|
||||
@@ -155,6 +156,9 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
case RUNNING:
|
||||
handleRunningRollout((JpaRollout) rollout);
|
||||
break;
|
||||
case STOPPING:
|
||||
handleStopRollout((JpaRollout) rollout);
|
||||
break;
|
||||
default:
|
||||
LOGGER.error("Rollout in status {} not supposed to be handled!", rollout.getStatus());
|
||||
break;
|
||||
@@ -244,6 +248,39 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
sendRolloutGroupDeletedEvents(rollout);
|
||||
}
|
||||
|
||||
private void handleStopRollout(final JpaRollout rollout) {
|
||||
LOGGER.debug("handleStopRollout called for {}", rollout.getId());
|
||||
// clean up all scheduled actions
|
||||
final Slice<JpaAction> scheduledActions = findScheduledActionsByRollout(rollout);
|
||||
deleteScheduledActions(rollout, scheduledActions);
|
||||
|
||||
// avoid another scheduler round and re-check if all scheduled actions
|
||||
// has been cleaned up. we flush first to ensure that the we include the
|
||||
// deletion above
|
||||
entityManager.flush();
|
||||
final boolean hasScheduledActionsLeft = actionRepository.countByRolloutIdAndStatus(rollout.getId(),
|
||||
Status.SCHEDULED) > 0;
|
||||
|
||||
if (hasScheduledActionsLeft) {
|
||||
return;
|
||||
}
|
||||
|
||||
rolloutGroupRepository.findByRolloutAndStatusNotIn(rollout,
|
||||
Arrays.asList(RolloutGroupStatus.FINISHED, RolloutGroupStatus.ERROR)).forEach(rolloutGroup -> {
|
||||
rolloutGroup.setStatus(RolloutGroupStatus.FINISHED);
|
||||
rolloutGroupRepository.save(rolloutGroup);
|
||||
});
|
||||
|
||||
rollout.setStatus(RolloutStatus.FINISHED);
|
||||
rolloutRepository.save(rollout);
|
||||
|
||||
final List<Long> groupIds = rollout.getRolloutGroups().stream().map(RolloutGroup::getId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
afterCommit.afterCommit(() -> eventPublisherHolder.getEventPublisher().publishEvent(new RolloutStoppedEvent(
|
||||
tenantAware.getCurrentTenant(), eventPublisherHolder.getApplicationId(), rollout.getId(), groupIds)));
|
||||
}
|
||||
|
||||
private void handleReadyRollout(final Rollout rollout) {
|
||||
if (rollout.getStartAt() != null && rollout.getStartAt() <= System.currentTimeMillis()) {
|
||||
LOGGER.debug(
|
||||
|
||||
@@ -100,7 +100,12 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(JpaRolloutManagement.class);
|
||||
|
||||
private static final List<RolloutStatus> ACTIVE_ROLLOUTS = Arrays.asList(RolloutStatus.CREATING,
|
||||
RolloutStatus.DELETING, RolloutStatus.STARTING, RolloutStatus.READY, RolloutStatus.RUNNING);
|
||||
RolloutStatus.DELETING, RolloutStatus.STARTING, RolloutStatus.READY, RolloutStatus.RUNNING,
|
||||
RolloutStatus.STOPPING);
|
||||
|
||||
private static final List<RolloutStatus> ROLLOUT_STATUS_STOPPABLE = Arrays.asList(RolloutStatus.RUNNING,
|
||||
RolloutStatus.CREATING, RolloutStatus.PAUSED, RolloutStatus.READY, RolloutStatus.STARTING,
|
||||
RolloutStatus.WAITING_FOR_APPROVAL, RolloutStatus.APPROVAL_DENIED);
|
||||
|
||||
@Autowired
|
||||
private RolloutRepository rolloutRepository;
|
||||
@@ -126,8 +131,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
|
||||
private final Database database;
|
||||
|
||||
public JpaRolloutManagement(final TargetManagement targetManagement, final DeploymentManagement deploymentManagement,
|
||||
final RolloutGroupManagement rolloutGroupManagement,
|
||||
public JpaRolloutManagement(final TargetManagement targetManagement,
|
||||
final DeploymentManagement deploymentManagement, final RolloutGroupManagement rolloutGroupManagement,
|
||||
final DistributionSetManagement distributionSetManagement, final ApplicationContext context,
|
||||
final EventPublisherHolder eventPublisherHolder, final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||
final PlatformTransactionManager txManager, final TenantAware tenantAware, final LockRegistry lockRegistry,
|
||||
@@ -151,7 +156,8 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
@Override
|
||||
public Page<Rollout> findByRsql(final Pageable pageable, final String rsqlParam, final boolean deleted) {
|
||||
final List<Specification<JpaRollout>> specList = Lists.newArrayListWithExpectedSize(2);
|
||||
specList.add(RSQLUtility.buildRsqlSpecification(rsqlParam, RolloutFields.class, virtualPropertyReplacer, database));
|
||||
specList.add(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlParam, RolloutFields.class, virtualPropertyReplacer, database));
|
||||
specList.add(RolloutSpecification.isDeletedWithDistributionSet(deleted));
|
||||
|
||||
return JpaRolloutHelper.convertPage(findByCriteriaAPI(pageable, specList), pageable);
|
||||
@@ -400,7 +406,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
|
||||
final String tenant = tenantAware.getCurrentTenant();
|
||||
|
||||
final String handlerId = tenant + "-rollout";
|
||||
final String handlerId = createRolloutLockKey(tenant);
|
||||
final Lock lock = lockRegistry.obtain(handlerId);
|
||||
if (!lock.tryLock()) {
|
||||
return;
|
||||
@@ -414,6 +420,10 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
}
|
||||
}
|
||||
|
||||
public static String createRolloutLockKey(final String tenant) {
|
||||
return tenant + "-rollout";
|
||||
}
|
||||
|
||||
private long handleRollout(final long rolloutId) {
|
||||
final JpaRollout rollout = rolloutRepository.findById(rolloutId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Rollout.class, rolloutId));
|
||||
@@ -451,6 +461,11 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
return rolloutRepository.count(JpaRolloutHelper.likeNameOrDescription(searchText, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByDistributionSetIdAndRolloutIsStoppable(final long setId) {
|
||||
return rolloutRepository.countByDistributionSetIdAndStatusIn(setId, ROLLOUT_STATUS_STOPPABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Rollout> findByFiltersWithDetailedStatus(final Pageable pageable, final String searchText,
|
||||
final boolean deleted) {
|
||||
@@ -483,8 +498,7 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
// null
|
||||
rollout.setStartAt(update.getStartAt().orElse(null));
|
||||
update.getSet().ifPresent(setId -> {
|
||||
final DistributionSet set = distributionSetManagement.get(setId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, setId));
|
||||
final DistributionSet set = distributionSetManagement.getValidAndComplete(setId);
|
||||
|
||||
rollout.setDistributionSet(set);
|
||||
});
|
||||
@@ -596,7 +610,20 @@ public class JpaRolloutManagement extends AbstractRolloutManagement {
|
||||
}
|
||||
|
||||
private void runInUserContext(final BaseEntity rollout, final Runnable handler) {
|
||||
DeploymentHelper.runInNonSystemContext(handler, () -> Objects.requireNonNull(rollout.getCreatedBy()), tenantAware);
|
||||
DeploymentHelper.runInNonSystemContext(handler, () -> Objects.requireNonNull(rollout.getCreatedBy()),
|
||||
tenantAware);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void cancelRolloutsForDistributionSet(final DistributionSet set) {
|
||||
// stop all rollouts for this distribution set
|
||||
rolloutRepository.findByDistributionSetAndStatusIn(set, ROLLOUT_STATUS_STOPPABLE).forEach(rollout -> {
|
||||
final JpaRollout jpaRollout = (JpaRollout) rollout;
|
||||
jpaRollout.setStatus(RolloutStatus.STOPPING);
|
||||
rolloutRepository.save(jpaRollout);
|
||||
LOGGER.debug("Rollout {} stopped", jpaRollout.getId());
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryUpdate;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.jpa.builder.JpaTargetFilterQueryCreate;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
@@ -147,6 +146,11 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
||||
return targetFilterQueryRepository.count();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByAutoAssignDistributionSetId(final long autoAssignDistributionSetId) {
|
||||
return targetFilterQueryRepository.countByAutoAssignDistributionSetId(autoAssignDistributionSetId);
|
||||
}
|
||||
|
||||
private static Page<TargetFilterQuery> convertPage(final Page<JpaTargetFilterQuery> findAll,
|
||||
final Pageable pageable) {
|
||||
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
|
||||
@@ -165,8 +169,8 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
||||
public Page<TargetFilterQuery> findByRsql(final Pageable pageable, final String rsqlFilter) {
|
||||
List<Specification<JpaTargetFilterQuery>> specList = Collections.emptyList();
|
||||
if (!StringUtils.isEmpty(rsqlFilter)) {
|
||||
specList = Collections.singletonList(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlFilter, TargetFilterQueryFields.class, virtualPropertyReplacer, database));
|
||||
specList = Collections.singletonList(RSQLUtility.buildRsqlSpecification(rsqlFilter,
|
||||
TargetFilterQueryFields.class, virtualPropertyReplacer, database));
|
||||
}
|
||||
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
|
||||
}
|
||||
@@ -185,13 +189,13 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
||||
final String rsqlFilter) {
|
||||
final List<Specification<JpaTargetFilterQuery>> specList = Lists.newArrayListWithExpectedSize(2);
|
||||
|
||||
final DistributionSet distributionSet = findDistributionSetAndThrowExceptionIfNotFound(setId);
|
||||
final DistributionSet distributionSet = distributionSetManagement.getOrElseThrowException(setId);
|
||||
|
||||
specList.add(TargetFilterQuerySpecification.byAutoAssignDS(distributionSet));
|
||||
|
||||
if (!StringUtils.isEmpty(rsqlFilter)) {
|
||||
specList.add(
|
||||
RSQLUtility.buildRsqlSpecification(rsqlFilter, TargetFilterQueryFields.class, virtualPropertyReplacer, database));
|
||||
specList.add(RSQLUtility.buildRsqlSpecification(rsqlFilter, TargetFilterQueryFields.class,
|
||||
virtualPropertyReplacer, database));
|
||||
}
|
||||
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
|
||||
}
|
||||
@@ -264,8 +268,9 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
||||
// specify an
|
||||
// auto-assign distribution set when creating a target filter query
|
||||
assertMaxTargetsQuota(targetFilterQuery.getQuery());
|
||||
final JpaDistributionSet ds = findDistributionSetAndThrowExceptionIfNotFound(update.getDsId());
|
||||
verifyDistributionSetAndThrowExceptionIfNotValid(ds);
|
||||
final JpaDistributionSet ds = (JpaDistributionSet) distributionSetManagement
|
||||
.getValidAndComplete(update.getDsId());
|
||||
verifyDistributionSetAndThrowExceptionIfDeleted(ds);
|
||||
targetFilterQuery.setAutoAssignDistributionSet(ds);
|
||||
targetFilterQuery.setAutoAssignInitiatedBy(tenantAware.getCurrentUsername());
|
||||
targetFilterQuery.setAutoAssignActionType(sanitizeAutoAssignActionType(update.getActionType()));
|
||||
@@ -274,9 +279,9 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
||||
return targetFilterQueryRepository.save(targetFilterQuery);
|
||||
}
|
||||
|
||||
private static void verifyDistributionSetAndThrowExceptionIfNotValid(final DistributionSet distributionSet) {
|
||||
if (!distributionSet.isComplete() || distributionSet.isDeleted()) {
|
||||
throw new InvalidAutoAssignDistributionSetException();
|
||||
private static void verifyDistributionSetAndThrowExceptionIfDeleted(final DistributionSet distributionSet) {
|
||||
if (distributionSet.isDeleted()) {
|
||||
throw new EntityNotFoundException(DistributionSet.class, distributionSet.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,11 +297,6 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
||||
return actionType;
|
||||
}
|
||||
|
||||
private JpaDistributionSet findDistributionSetAndThrowExceptionIfNotFound(final Long setId) {
|
||||
return (JpaDistributionSet) distributionSetManagement.get(setId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, setId));
|
||||
}
|
||||
|
||||
private JpaTargetFilterQuery findTargetFilterQueryOrThrowExceptionIfNotFound(final Long queryId) {
|
||||
return targetFilterQueryRepository.findById(queryId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(TargetFilterQuery.class, queryId));
|
||||
@@ -317,4 +317,11 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
|
||||
QuotaHelper.assertAssignmentQuota(targetManagement.countByRsql(query),
|
||||
quotaManagement.getMaxTargetsPerAutoAssignment(), Target.class, TargetFilterQuery.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void cancelAutoAssignmentForDistributionSet(final long setId) {
|
||||
targetFilterQueryRepository.unsetAutoAssignDistributionSetAndActionType(setId);
|
||||
LOGGER.debug("Auto assignments for distribution sets {} deactivated", setId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,8 +82,8 @@ import org.eclipse.hawkbit.repository.jpa.rollout.condition.PauseRolloutGroupAct
|
||||
import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRolloutGroupSuccessAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupErrorCondition;
|
||||
import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupSuccessCondition;
|
||||
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlParserValidationOracle;
|
||||
import org.eclipse.hawkbit.repository.jpa.rsql.DefaultRsqlVisitorFactory;
|
||||
import org.eclipse.hawkbit.repository.jpa.rsql.RsqlParserValidationOracle;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
@@ -244,7 +244,8 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
|
||||
/**
|
||||
* @param dsTypeManagement
|
||||
* for loading {@link TargetType#getCompatibleDistributionSetTypes()}
|
||||
* for loading
|
||||
* {@link TargetType#getCompatibleDistributionSetTypes()}
|
||||
* @return TargetTypeBuilder bean
|
||||
*/
|
||||
@Bean
|
||||
@@ -434,7 +435,7 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
@Override
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager(
|
||||
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
|
||||
final ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
|
||||
return new MultiTenantJpaTransactionManager();
|
||||
}
|
||||
|
||||
@@ -485,12 +486,12 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
DistributionSetTypeManagement distributionSetTypeManagement(
|
||||
final DistributionSetTypeRepository distributionSetTypeRepository,
|
||||
final SoftwareModuleTypeRepository softwareModuleTypeRepository,
|
||||
final DistributionSetRepository distributionSetRepository,
|
||||
final TargetTypeRepository targetTypeRepository,
|
||||
final DistributionSetRepository distributionSetRepository, final TargetTypeRepository targetTypeRepository,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties,
|
||||
final QuotaManagement quotaManagement) {
|
||||
return new JpaDistributionSetTypeManagement(distributionSetTypeRepository, softwareModuleTypeRepository,
|
||||
distributionSetRepository, targetTypeRepository, virtualPropertyReplacer, properties.getDatabase(), quotaManagement);
|
||||
distributionSetRepository, targetTypeRepository, virtualPropertyReplacer, properties.getDatabase(),
|
||||
quotaManagement);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -541,13 +542,15 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
final TargetRepository targetRepository, final TargetMetadataRepository targetMetadataRepository,
|
||||
final RolloutGroupRepository rolloutGroupRepository,
|
||||
final DistributionSetRepository distributionSetRepository,
|
||||
final TargetFilterQueryRepository targetFilterQueryRepository, final TargetTypeRepository targetTypeRepository,
|
||||
final TargetTagRepository targetTagRepository, final EventPublisherHolder eventPublisherHolder,
|
||||
final TenantAware tenantAware, final AfterTransactionCommitExecutor afterCommit,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer, final JpaProperties properties) {
|
||||
return new JpaTargetManagement(entityManager, quotaManagement, targetRepository, targetTypeRepository, targetMetadataRepository,
|
||||
rolloutGroupRepository, distributionSetRepository, targetFilterQueryRepository, targetTagRepository,
|
||||
eventPublisherHolder, tenantAware, afterCommit, virtualPropertyReplacer, properties.getDatabase());
|
||||
final TargetFilterQueryRepository targetFilterQueryRepository,
|
||||
final TargetTypeRepository targetTypeRepository, final TargetTagRepository targetTagRepository,
|
||||
final EventPublisherHolder eventPublisherHolder, final TenantAware tenantAware,
|
||||
final AfterTransactionCommitExecutor afterCommit, final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||
final JpaProperties properties) {
|
||||
return new JpaTargetManagement(entityManager, quotaManagement, targetRepository, targetTypeRepository,
|
||||
targetMetadataRepository, rolloutGroupRepository, distributionSetRepository,
|
||||
targetFilterQueryRepository, targetTagRepository, eventPublisherHolder, tenantAware, afterCommit,
|
||||
virtualPropertyReplacer, properties.getDatabase());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -718,17 +721,17 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
@ConditionalOnMissingBean
|
||||
DeploymentManagement deploymentManagement(final EntityManager entityManager,
|
||||
final ActionRepository actionRepository, final DistributionSetRepository distributionSetRepository,
|
||||
final TargetRepository targetRepository, final ActionStatusRepository actionStatusRepository,
|
||||
final AuditorAware<String> auditorProvider, final EventPublisherHolder eventPublisherHolder,
|
||||
final AfterTransactionCommitExecutor afterCommit, final VirtualPropertyReplacer virtualPropertyReplacer,
|
||||
final PlatformTransactionManager txManager,
|
||||
final DistributionSetManagement distributionSetManagement, final TargetRepository targetRepository,
|
||||
final ActionStatusRepository actionStatusRepository, final AuditorAware<String> auditorProvider,
|
||||
final EventPublisherHolder eventPublisherHolder, final AfterTransactionCommitExecutor afterCommit,
|
||||
final VirtualPropertyReplacer virtualPropertyReplacer, final PlatformTransactionManager txManager,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement, final QuotaManagement quotaManagement,
|
||||
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware,
|
||||
final JpaProperties properties, final RepositoryProperties repositoryProperties) {
|
||||
return new JpaDeploymentManagement(entityManager, actionRepository, distributionSetRepository, targetRepository,
|
||||
actionStatusRepository, auditorProvider, eventPublisherHolder, afterCommit, virtualPropertyReplacer,
|
||||
txManager, tenantConfigurationManagement, quotaManagement, systemSecurityContext, tenantAware,
|
||||
properties.getDatabase(), repositoryProperties);
|
||||
return new JpaDeploymentManagement(entityManager, actionRepository, distributionSetManagement,
|
||||
distributionSetRepository, targetRepository, actionStatusRepository, auditorProvider,
|
||||
eventPublisherHolder, afterCommit, virtualPropertyReplacer, txManager, tenantConfigurationManagement,
|
||||
quotaManagement, systemSecurityContext, tenantAware, properties.getDatabase(), repositoryProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -925,4 +928,22 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
|
||||
return RsqlVisitorFactoryHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link JpaDistributionSetInvalidationManagement} bean.
|
||||
*
|
||||
* @return a new {@link JpaDistributionSetInvalidationManagement}
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
JpaDistributionSetInvalidationManagement distributionSetInvalidationManagement(
|
||||
final DistributionSetManagement distributionSetManagement, final RolloutManagement rolloutManagement,
|
||||
final DeploymentManagement deploymentManagement,
|
||||
final TargetFilterQueryManagement targetFilterQueryManagement, final PlatformTransactionManager txManager,
|
||||
final RepositoryProperties repositoryProperties, final TenantAware tenantAware,
|
||||
final LockRegistry lockRegistry) {
|
||||
return new JpaDistributionSetInvalidationManagement(distributionSetManagement, rolloutManagement,
|
||||
deploymentManagement, targetFilterQueryManagement, txManager, repositoryProperties, tenantAware,
|
||||
lockRegistry);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
@@ -135,6 +136,19 @@ public interface RolloutGroupRepository
|
||||
*/
|
||||
List<JpaRolloutGroup> findByRolloutAndStatusNotOrderByIdDesc(JpaRollout rollout, RolloutGroupStatus notStatus);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link RolloutGroup}s for a specific rollout and status not
|
||||
* having.
|
||||
*
|
||||
* @param rollout
|
||||
* the rollout the rolloutgroup belong to
|
||||
* @param status
|
||||
* the status which the rolloutgroup should not have
|
||||
* @return rolloutgroup referring to a rollout and not having a specific
|
||||
* status.
|
||||
*/
|
||||
List<JpaRolloutGroup> findByRolloutAndStatusNotIn(JpaRollout rollout, Collection<RolloutGroupStatus> status);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link RolloutGroup} for a specific rollout.
|
||||
*
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.Optional;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
@@ -64,4 +65,28 @@ public interface RolloutRepository
|
||||
@Transactional
|
||||
@Query("DELETE FROM JpaRollout t WHERE t.tenant = :tenant")
|
||||
void deleteByTenant(@Param("tenant") String tenant);
|
||||
|
||||
/**
|
||||
* Retrieves all {@link Rollout}s for a specific {@link DistributionSet} in
|
||||
* a given {@link RolloutStatus}.
|
||||
*
|
||||
* @param set
|
||||
* the distribution set
|
||||
* @param status
|
||||
* the status of the rollout
|
||||
* @return {@link Rollout} for specific distribution set
|
||||
*/
|
||||
List<Rollout> findByDistributionSetAndStatusIn(DistributionSet set, Collection<RolloutStatus> status);
|
||||
|
||||
/**
|
||||
* Counts all {@link Rollout}s for a specific {@link DistributionSet} in a
|
||||
* given {@link RolloutStatus}.
|
||||
*
|
||||
* @param distributionSetId
|
||||
* the distribution set
|
||||
* @param status
|
||||
* the status of the rollout
|
||||
* @return the count
|
||||
*/
|
||||
long countByDistributionSetIdAndStatusIn(long distributionSetId, Collection<RolloutStatus> status);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,16 @@ public interface TargetFilterQueryRepository
|
||||
@Query("update JpaTargetFilterQuery d set d.autoAssignDistributionSet = NULL, d.autoAssignActionType = NULL where d.autoAssignDistributionSet in :ids")
|
||||
void unsetAutoAssignDistributionSetAndActionType(@Param("ids") Long... dsIds);
|
||||
|
||||
/**
|
||||
* Counts all target filters that have a given auto assign distribution set
|
||||
* assigned.
|
||||
*
|
||||
* @param autoAssignDistributionSetId
|
||||
* the id of the distribution set
|
||||
* @return the count
|
||||
*/
|
||||
long countByAutoAssignDistributionSetId(long autoAssignDistributionSetId);
|
||||
|
||||
/**
|
||||
* Deletes all {@link TenantAwareBaseEntity} of a given tenant. For safety
|
||||
* reasons (this is a "delete everything" query after all) we add the tenant
|
||||
|
||||
@@ -11,9 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.builder;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.builder.AbstractRolloutUpdateCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
|
||||
public class JpaRolloutCreate extends AbstractRolloutUpdateCreate<RolloutCreate> implements RolloutCreate {
|
||||
private final DistributionSetManagement distributionSetManagement;
|
||||
@@ -28,7 +26,7 @@ public class JpaRolloutCreate extends AbstractRolloutUpdateCreate<RolloutCreate>
|
||||
|
||||
rollout.setName(name);
|
||||
rollout.setDescription(description);
|
||||
rollout.setDistributionSet(findDistributionSetAndThrowExceptionIfNotFound(set));
|
||||
rollout.setDistributionSet(distributionSetManagement.getValidAndComplete(set));
|
||||
rollout.setTargetFilterQuery(targetFilterQuery);
|
||||
rollout.setStartAt(startAt);
|
||||
rollout.setWeight(weight);
|
||||
@@ -43,9 +41,4 @@ public class JpaRolloutCreate extends AbstractRolloutUpdateCreate<RolloutCreate>
|
||||
|
||||
return rollout;
|
||||
}
|
||||
|
||||
private DistributionSet findDistributionSetAndThrowExceptionIfNotFound(final Long setId) {
|
||||
return distributionSetManagement.get(setId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, setId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,9 @@ package org.eclipse.hawkbit.repository.jpa.builder;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.builder.AbstractTargetFilterQueryUpdateCreate;
|
||||
import org.eclipse.hawkbit.repository.builder.TargetFilterQueryCreate;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
|
||||
/**
|
||||
@@ -35,16 +33,11 @@ public class JpaTargetFilterQueryCreate extends AbstractTargetFilterQueryUpdateC
|
||||
public JpaTargetFilterQuery build() {
|
||||
|
||||
return new JpaTargetFilterQuery(name, query,
|
||||
getAutoAssignDistributionSetId().map(this::findDistributionSetAndThrowExceptionIfNotFound).orElse(null),
|
||||
getAutoAssignDistributionSetId().map(distributionSetManagement::getValidAndComplete).orElse(null),
|
||||
getAutoAssignActionType().filter(JpaTargetFilterQueryCreate::isAutoAssignActionTypeValid).orElse(null),
|
||||
weight);
|
||||
}
|
||||
|
||||
private DistributionSet findDistributionSetAndThrowExceptionIfNotFound(final Long setId) {
|
||||
return distributionSetManagement.get(setId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, setId));
|
||||
}
|
||||
|
||||
private static boolean isAutoAssignActionTypeValid(final ActionType actionType) {
|
||||
if (!TargetFilterQuery.ALLOWED_AUTO_ASSIGN_ACTION_TYPES.contains(actionType)) {
|
||||
throw new InvalidAutoAssignActionTypeException();
|
||||
|
||||
@@ -116,6 +116,9 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
||||
@Column(name = "complete")
|
||||
private boolean complete;
|
||||
|
||||
@Column(name = "valid")
|
||||
private boolean valid;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
@@ -145,6 +148,7 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
||||
super(name, version, description);
|
||||
|
||||
this.requiredMigrationStep = requiredMigrationStep;
|
||||
this.valid = true;
|
||||
this.type = type;
|
||||
if (moduleList != null) {
|
||||
moduleList.forEach(this::addModule);
|
||||
@@ -327,6 +331,15 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
|
||||
return complete;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
this.valid = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireCreateEvent(final DescriptorEvent descriptorEvent) {
|
||||
publishEventWithEventPublisher(
|
||||
|
||||
@@ -93,7 +93,8 @@ public class JpaRollout extends AbstractJpaNamedEntity implements Rollout, Event
|
||||
@ConversionValue(objectValue = "DELETING", dataValue = "9"),
|
||||
@ConversionValue(objectValue = "DELETED", dataValue = "10"),
|
||||
@ConversionValue(objectValue = "WAITING_FOR_APPROVAL", dataValue = "11"),
|
||||
@ConversionValue(objectValue = "APPROVAL_DENIED", dataValue = "12") })
|
||||
@ConversionValue(objectValue = "APPROVAL_DENIED", dataValue = "12"),
|
||||
@ConversionValue(objectValue = "STOPPING", dataValue = "13") })
|
||||
@Convert("rolloutstatus")
|
||||
@NotNull
|
||||
private RolloutStatus status = RolloutStatus.CREATING;
|
||||
|
||||
@@ -70,6 +70,20 @@ public final class DistributionSetSpecification {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link DistributionSet}s by its
|
||||
* VALID attribute.
|
||||
*
|
||||
* @param isValid
|
||||
* TRUE/FALSE are compared to the attribute VALID. If NULL the
|
||||
* attribute is ignored
|
||||
* @return the {@link DistributionSet} {@link Specification}
|
||||
*/
|
||||
public static Specification<JpaDistributionSet> isValid(final Boolean isValid) {
|
||||
return (targetRoot, query, cb) -> cb.equal(targetRoot.<Boolean> get(JpaDistributionSet_.valid), isValid);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Specification} for retrieving {@link DistributionSet} with given
|
||||
* {@link DistributionSet#getId()}.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE sp_distribution_set ADD COLUMN valid BOOLEAN;
|
||||
|
||||
UPDATE sp_distribution_set SET valid = 1;
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE sp_distribution_set ADD COLUMN valid BOOLEAN;
|
||||
|
||||
UPDATE sp_distribution_set SET valid = 1;
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE sp_distribution_set ADD COLUMN valid BOOLEAN;
|
||||
|
||||
UPDATE sp_distribution_set SET valid = 1;
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE sp_distribution_set ADD COLUMN valid BOOLEAN;
|
||||
|
||||
UPDATE sp_distribution_set SET valid = 1;
|
||||
@@ -0,0 +1,3 @@
|
||||
ALTER TABLE sp_distribution_set ADD COLUMN valid BOOLEAN;
|
||||
|
||||
UPDATE sp_distribution_set SET valid = 1;
|
||||
@@ -8,10 +8,13 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
@@ -34,8 +37,11 @@ public class RolloutEventTest extends AbstractRemoteEntityEventTest<Rollout> {
|
||||
@Override
|
||||
protected Rollout createEntity() {
|
||||
testdataFactory.createTarget("12345");
|
||||
final DistributionSet ds = distributionSetManagement.create(entityFactory.distributionSet()
|
||||
.create().name("incomplete").version("2").description("incomplete").type("os"));
|
||||
final SoftwareModule module = softwareModuleManagement.create(
|
||||
entityFactory.softwareModule().create().name("swm").version("2").description("desc").type("os"));
|
||||
final DistributionSet ds = distributionSetManagement
|
||||
.create(entityFactory.distributionSet().create().name("complete").version("2").description("complete")
|
||||
.type("os").modules(Collections.singletonList(module.getId())));
|
||||
|
||||
return rolloutManagement.create(
|
||||
entityFactory.rollout().create().name("exampleRollout").targetFilterQuery("controllerId==*").set(ds),
|
||||
|
||||
@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.event.remote.entity;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
@@ -17,6 +18,7 @@ import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
@@ -75,9 +77,11 @@ public class RolloutGroupEventTest extends AbstractRemoteEntityEventTest<Rollout
|
||||
@Override
|
||||
protected RolloutGroup createEntity() {
|
||||
testdataFactory.createTarget(UUID.randomUUID().toString());
|
||||
|
||||
final DistributionSet ds = distributionSetManagement.create(entityFactory.distributionSet().create()
|
||||
.name("incomplete").version("2").description("incomplete").type("os"));
|
||||
final SoftwareModule module = softwareModuleManagement.create(
|
||||
entityFactory.softwareModule().create().name("swm").version("2").description("desc").type("os"));
|
||||
final DistributionSet ds = distributionSetManagement
|
||||
.create(entityFactory.distributionSet().create().name("complete").version("2").description("complete")
|
||||
.type("os").modules(Collections.singletonList(module.getId())));
|
||||
|
||||
final Rollout entity = rolloutManagement.create(
|
||||
entityFactory.rollout().create().name("exampleRollout").targetFilterQuery("controllerId==*").set(ds),
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO 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 static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.awaitility.Awaitility;
|
||||
import org.eclipse.hawkbit.repository.exception.StopRolloutException;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorAction;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorCondition;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupConditionBuilder;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroupConditions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
|
||||
/**
|
||||
* Test class testing the invalidation of a {@link DistributionSet} while the
|
||||
* handle rollouts is ongoing.
|
||||
*
|
||||
*/
|
||||
@Feature("Component Tests - Repository")
|
||||
@Story("Concurrent Distribution Set invalidation")
|
||||
@TestPropertySource(properties = "hawkbit.server.repository.dsInvalidationLockTimeout=1")
|
||||
public class ConcurrentDistributionSetInvalidationTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Verify that a large rollout causes a timeout when trying to invalidate a distribution set")
|
||||
public void verifyInvalidateDistributionSetWithLargeRolloutThrowsException() throws Exception {
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
testdataFactory.createTargets(10000, "verifyInvalidateDistributionSetWithLargeRolloutThrowsException");
|
||||
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults()
|
||||
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "50")
|
||||
.errorCondition(RolloutGroupErrorCondition.THRESHOLD, "80")
|
||||
.errorAction(RolloutGroupErrorAction.PAUSE, null).build();
|
||||
final Rollout rollout = rolloutManagement.create(entityFactory.rollout().create()
|
||||
.name("verifyInvalidateDistributionSetWithLargeRolloutThrowsException").description("desc")
|
||||
.targetFilterQuery("name==*").set(distributionSet).actionType(ActionType.FORCED), 20, conditions);
|
||||
final String tenant = tenantAware.getCurrentTenant();
|
||||
|
||||
// run in new Thread so that the invalidation can be executed in
|
||||
// parallel
|
||||
final Thread handleRolloutsThread = new Thread(() -> {
|
||||
tenantAware.runAsTenant(tenant, () -> systemSecurityContext.runAsSystem(() -> {
|
||||
rolloutManagement.handleRollouts();
|
||||
return 0;
|
||||
}));
|
||||
});
|
||||
handleRolloutsThread.start();
|
||||
// wait until at least one RolloutGroup is created, as this means that
|
||||
// the thread has started and has acquired the lock
|
||||
Awaitility.await().until(() -> tenantAware.runAsTenant(tenant, () -> systemSecurityContext
|
||||
.runAsSystem(() -> rolloutGroupManagement.findByRollout(PAGE, rollout.getId()).getSize() > 0)));
|
||||
|
||||
assertThatExceptionOfType(StopRolloutException.class)
|
||||
.as("Invalidation of distributionSet should throw an exception")
|
||||
.isThrownBy(() -> distributionSetInvalidationManagement.invalidateDistributionSet(
|
||||
new DistributionSetInvalidation(Collections.singletonList(distributionSet.getId()),
|
||||
CancelationType.SOFT, true)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
@@ -844,8 +845,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
/**
|
||||
* test a simple deployment by calling the
|
||||
* {@link TargetRepository#assignDistributionSet(DistributionSet, Iterable)} and
|
||||
* checking the active action and the action history of the targets.
|
||||
* {@link TargetRepository#assignDistributionSet(DistributionSet, Iterable)}
|
||||
* and checking the active action and the action history of the targets.
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
@@ -1088,9 +1089,10 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* test the deletion of {@link DistributionSet}s including exception in case of
|
||||
* {@link Target}s are assigned by {@link Target#getAssignedDistributionSet()}
|
||||
* or {@link Target#getInstalledDistributionSet()}
|
||||
* test the deletion of {@link DistributionSet}s including exception in case
|
||||
* of {@link Target}s are assigned by
|
||||
* {@link Target#getAssignedDistributionSet()} or
|
||||
* {@link Target#getInstalledDistributionSet()}
|
||||
*/
|
||||
@Test
|
||||
@Description("Deletes distribution set. Expected behaviour is that a soft delete is performed "
|
||||
@@ -1357,12 +1359,36 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(distributionSetRepository.findAll()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests that an exception is thrown when a target is assigned to an incomplete distribution set")
|
||||
public void verifyAssignTargetsToIncompleteDistribution() {
|
||||
final DistributionSet distributionSet = testdataFactory.createIncompleteDistributionSet();
|
||||
final Target target = testdataFactory.createTarget();
|
||||
|
||||
assertThatExceptionOfType(IncompleteDistributionSetException.class)
|
||||
.as("Incomplete distributionSet should throw an exception")
|
||||
.isThrownBy(() -> assignDistributionSet(distributionSet, target));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Tests that an exception is thrown when a target is assigned to an invalidated distribution set")
|
||||
public void verifyAssignTargetsToInvalidDistribution() {
|
||||
final DistributionSet distributionSet = testdataFactory.createAndInvalidateDistributionSet();
|
||||
final Target target = testdataFactory.createTarget();
|
||||
|
||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||
.as("Invalid distributionSet should throw an exception")
|
||||
.isThrownBy(() -> assignDistributionSet(distributionSet, target));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper methods that creates 2 lists of targets and a list of distribution
|
||||
* sets.
|
||||
* <p>
|
||||
* <b>All created distribution sets are assigned to all targets of the target
|
||||
* list deployedTargets.</b>
|
||||
* <b>All created distribution sets are assigned to all targets of the
|
||||
* target list deployedTargets.</b>
|
||||
*
|
||||
* @param undeployedTargetPrefix
|
||||
* prefix to be used as target controller prefix
|
||||
@@ -1371,7 +1397,8 @@ public class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
* @param deployedTargetPrefix
|
||||
* prefix to be used as target controller prefix
|
||||
* @param noOfDeployedTargets
|
||||
* number of targets to which the created distribution sets assigned
|
||||
* number of targets to which the created distribution sets
|
||||
* assigned
|
||||
* @param noOfDistributionSets
|
||||
* number of distribution sets
|
||||
* @param distributionSetPrefix
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO 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 static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidationCount;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Story;
|
||||
|
||||
/**
|
||||
* Test class testing the functionality of invalidating a
|
||||
* {@link DistributionSet}
|
||||
*
|
||||
*/
|
||||
@Feature("Component Tests - Repository")
|
||||
@Story("Distribution set invalidation management")
|
||||
class DistributionSetInvalidationManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Test
|
||||
@Description("Verify invalidation of distribution sets that only removes distribution sets from auto assignments")
|
||||
void verifyInvalidateDistributionSetStopAutoAssignment() {
|
||||
final InvalidationTestData invalidationTestData = createInvalidationTestData(
|
||||
"verifyInvalidateDistributionSetStopAutoAssignment");
|
||||
|
||||
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), CancelationType.NONE,
|
||||
false);
|
||||
final DistributionSetInvalidationCount distributionSetInvalidationCount = distributionSetInvalidationManagement
|
||||
.countEntitiesForInvalidation(distributionSetInvalidation);
|
||||
assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 0, 0);
|
||||
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation);
|
||||
rolloutManagement.handleRollouts();
|
||||
|
||||
assertThat(targetFilterQueryManagement.get(invalidationTestData.getTargetFilterQuery().getId()).get()
|
||||
.getAutoAssignDistributionSet()).isNull();
|
||||
assertThat(rolloutRepository.findById(invalidationTestData.getRollout().getId()).get().getStatus())
|
||||
.isNotIn(RolloutStatus.STOPPING, RolloutStatus.FINISHED);
|
||||
for (final Target target : invalidationTestData.getTargets()) {
|
||||
// if status is pending, the assignment has not been canceled
|
||||
assertThat(targetRepository.findById(target.getId()).get().getUpdateStatus())
|
||||
.isEqualTo(TargetUpdateStatus.PENDING);
|
||||
assertThat(actionRepository.findByTarget(target).size()).isEqualTo(1);
|
||||
assertThat(actionRepository.findByTarget(target).get(0).getStatus()).isEqualTo(Status.RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify invalidation of distribution sets that removes distribution sets from auto assignments and stops rollouts")
|
||||
void verifyInvalidateDistributionSetStopRollouts() {
|
||||
final InvalidationTestData invalidationTestData = createInvalidationTestData(
|
||||
"verifyInvalidateDistributionSetStopRollouts");
|
||||
|
||||
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), CancelationType.NONE,
|
||||
true);
|
||||
final DistributionSetInvalidationCount distributionSetInvalidationCount = distributionSetInvalidationManagement
|
||||
.countEntitiesForInvalidation(distributionSetInvalidation);
|
||||
assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 0, 1);
|
||||
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation);
|
||||
rolloutManagement.handleRollouts();
|
||||
|
||||
assertThat(targetFilterQueryManagement.get(invalidationTestData.getTargetFilterQuery().getId()).get()
|
||||
.getAutoAssignDistributionSet()).isNull();
|
||||
assertThat(rolloutRepository.findById(invalidationTestData.getRollout().getId()).get().getStatus())
|
||||
.isEqualTo(RolloutStatus.FINISHED);
|
||||
assertNoScheduledActionsExist(invalidationTestData.getRollout());
|
||||
assertRolloutGroupsAreFinished(invalidationTestData.getRollout());
|
||||
for (final Target target : invalidationTestData.getTargets()) {
|
||||
// if status is pending, the assignment has not been canceled
|
||||
assertThat(
|
||||
targetRepository.findById(invalidationTestData.getTargets().get(0).getId()).get().getUpdateStatus())
|
||||
.isEqualTo(TargetUpdateStatus.PENDING);
|
||||
assertThat(actionRepository.findByTarget(target).size()).isEqualTo(1);
|
||||
assertThat(actionRepository.findByTarget(target).get(0).getStatus()).isEqualTo(Status.RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify invalidation of distribution sets that removes distribution sets from auto assignments, stops rollouts and force cancels assignments")
|
||||
void verifyInvalidateDistributionSetStopAllAndForceCancel() {
|
||||
final InvalidationTestData invalidationTestData = createInvalidationTestData(
|
||||
"verifyInvalidateDistributionSetStopAllAndForceCancel");
|
||||
|
||||
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), CancelationType.FORCE,
|
||||
true);
|
||||
final DistributionSetInvalidationCount distributionSetInvalidationCount = distributionSetInvalidationManagement
|
||||
.countEntitiesForInvalidation(distributionSetInvalidation);
|
||||
assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 5, 1);
|
||||
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation);
|
||||
rolloutManagement.handleRollouts();
|
||||
|
||||
assertThat(targetFilterQueryManagement.get(invalidationTestData.getTargetFilterQuery().getId()).get()
|
||||
.getAutoAssignDistributionSet()).isNull();
|
||||
assertThat(rolloutRepository.findById(invalidationTestData.getRollout().getId()).get().getStatus())
|
||||
.isEqualTo(RolloutStatus.FINISHED);
|
||||
assertNoScheduledActionsExist(invalidationTestData.getRollout());
|
||||
assertRolloutGroupsAreFinished(invalidationTestData.getRollout());
|
||||
for (final Target target : invalidationTestData.getTargets()) {
|
||||
assertThat(targetRepository.findById(target.getId()).get().getUpdateStatus())
|
||||
.isEqualTo(TargetUpdateStatus.IN_SYNC);
|
||||
assertThat(actionRepository.findByTarget(target).size()).isEqualTo(1);
|
||||
assertThat(actionRepository.findByTarget(target).get(0).getStatus()).isEqualTo(Status.CANCELED);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertNoScheduledActionsExist(final Rollout rollout) {
|
||||
assertThat(
|
||||
actionRepository.findByRolloutIdAndStatus(PAGE, rollout.getId(), Status.SCHEDULED).getTotalElements())
|
||||
.isZero();
|
||||
}
|
||||
|
||||
private void assertRolloutGroupsAreFinished(final Rollout rollout) {
|
||||
assertThat(rolloutGroupRepository.findByRolloutId(rollout.getId(), PAGE))
|
||||
.allMatch(rolloutGroup -> rolloutGroup.getStatus().equals(RolloutGroupStatus.FINISHED));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify invalidation of distribution sets that removes distribution sets from auto assignments, stops rollouts and cancels assignments")
|
||||
void verifyInvalidateDistributionSetStopAll() {
|
||||
final InvalidationTestData invalidationTestData = createInvalidationTestData(
|
||||
"verifyInvalidateDistributionSetStopAll");
|
||||
|
||||
final DistributionSetInvalidation distributionSetInvalidation = new DistributionSetInvalidation(
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), CancelationType.SOFT,
|
||||
true);
|
||||
final DistributionSetInvalidationCount distributionSetInvalidationCount = distributionSetInvalidationManagement
|
||||
.countEntitiesForInvalidation(distributionSetInvalidation);
|
||||
assertDistributionSetInvalidationCount(distributionSetInvalidationCount, 1, 5, 1);
|
||||
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(distributionSetInvalidation);
|
||||
|
||||
assertThat(targetFilterQueryManagement.get(invalidationTestData.getTargetFilterQuery().getId()).get()
|
||||
.getAutoAssignDistributionSet()).isNull();
|
||||
assertThat(rolloutRepository.findById(invalidationTestData.getRollout().getId()).get().getStatus())
|
||||
.isIn(RolloutStatus.STOPPING, RolloutStatus.FINISHED);
|
||||
for (final Target target : invalidationTestData.getTargets()) {
|
||||
assertThat(targetRepository.findById(target.getId()).get().getUpdateStatus())
|
||||
.isEqualTo(TargetUpdateStatus.PENDING);
|
||||
assertThat(actionRepository.findByTarget(target).size()).isEqualTo(1);
|
||||
assertThat(actionRepository.findByTarget(target).get(0).getStatus()).isEqualTo(Status.CANCELING);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that invalidating an incomplete distribution set throws an exception")
|
||||
void verifyInvalidateIncompleteDistributionSetThrowsException() {
|
||||
final DistributionSet distributionSet = testdataFactory.createIncompleteDistributionSet();
|
||||
|
||||
assertThatExceptionOfType(IncompleteDistributionSetException.class)
|
||||
.as("Incomplete distributionSet should throw an exception")
|
||||
.isThrownBy(() -> distributionSetInvalidationManagement.invalidateDistributionSet(
|
||||
new DistributionSetInvalidation(Collections.singletonList(distributionSet.getId()),
|
||||
CancelationType.SOFT, true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that invalidating an invalidated distribution set throws an exception")
|
||||
void verifyInvalidateInvalidatedDistributionSetThrowsException() {
|
||||
final DistributionSet distributionSet = testdataFactory.createAndInvalidateDistributionSet();
|
||||
|
||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||
.as("Invalid distributionSet should throw an exception")
|
||||
.isThrownBy(() -> distributionSetInvalidationManagement.invalidateDistributionSet(
|
||||
new DistributionSetInvalidation(Collections.singletonList(distributionSet.getId()),
|
||||
CancelationType.SOFT, true)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that a user that has authority READ_REPOSITORY and UPDATE_REPOSITORY is not allowed to invalidate a distribution set")
|
||||
@WithUser(authorities = { "READ_REPOSITORY", "UPDATE_REPOSITORY" })
|
||||
void verifyInvalidateWithReadAndUpdateRepoAuthority() {
|
||||
final InvalidationTestData invalidationTestData = systemSecurityContext
|
||||
.runAsSystem(() -> createInvalidationTestData("verifyInvalidateWithUpdateRepoAuthority"));
|
||||
|
||||
assertThatExceptionOfType(InsufficientPermissionException.class)
|
||||
.as("Insufficient permission exception expected")
|
||||
.isThrownBy(() -> distributionSetInvalidationManagement
|
||||
.invalidateDistributionSet(new DistributionSetInvalidation(
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()),
|
||||
CancelationType.NONE, false)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that a user that has authority READ_REPOSITORY, UPDATE_REPOSITORY and UPDATE_TARGET is allowed to invalidate a distribution set only without canceling rollouts")
|
||||
@WithUser(authorities = { "READ_REPOSITORY", "UPDATE_REPOSITORY", "UPDATE_TARGET" })
|
||||
void verifyInvalidateWithReadAndUpdateRepoAndUpdateTargetAuthority() {
|
||||
final InvalidationTestData invalidationTestData = systemSecurityContext.runAsSystem(
|
||||
() -> createInvalidationTestData("verifyInvalidateWithUpdateRepoAndUpdateTargetAuthority"));
|
||||
|
||||
assertThatExceptionOfType(InsufficientPermissionException.class)
|
||||
.as("Insufficient permission exception expected")
|
||||
.isThrownBy(() -> distributionSetInvalidationManagement
|
||||
.invalidateDistributionSet(new DistributionSetInvalidation(
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()),
|
||||
CancelationType.SOFT, true)));
|
||||
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(new DistributionSetInvalidation(
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), CancelationType.NONE,
|
||||
false));
|
||||
assertThat(
|
||||
distributionSetRepository.findById(invalidationTestData.getDistributionSet().getId()).get().isValid())
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that a user that has authority READ_REPOSITORY, UPDATE_REPOSITORY, UPDATE_ROLLOUT and UPDATE_TARGET is allowed to invalidate a distribution")
|
||||
@WithUser(authorities = { "READ_REPOSITORY", "UPDATE_REPOSITORY", "UPDATE_TARGET", "UPDATE_ROLLOUT" })
|
||||
void verifyInvalidateWithReadAndUpdateRepoAndUpdateTargetAndUpdateRolloutAuthority() {
|
||||
final InvalidationTestData invalidationTestData = systemSecurityContext.runAsSystem(
|
||||
() -> createInvalidationTestData("verifyInvalidateWithUpdateRepoAndUpdateTargetAuthority"));
|
||||
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(new DistributionSetInvalidation(
|
||||
Collections.singletonList(invalidationTestData.getDistributionSet().getId()), CancelationType.SOFT,
|
||||
true));
|
||||
assertThat(
|
||||
distributionSetRepository.findById(invalidationTestData.getDistributionSet().getId()).get().isValid())
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
private InvalidationTestData createInvalidationTestData(final String testName) {
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
final List<Target> targets = testdataFactory.createTargets(5, testName);
|
||||
assignDistributionSet(distributionSet, targets);
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement.create(entityFactory.targetFilterQuery()
|
||||
.create().name(testName).query("name==*").autoAssignDistributionSet(distributionSet));
|
||||
final Rollout rollout = testdataFactory.createRolloutByVariables(testName, "desc", 2, "name==*",
|
||||
distributionSet, "50", "80");
|
||||
|
||||
return new InvalidationTestData(distributionSet, targets, targetFilterQuery, rollout);
|
||||
}
|
||||
|
||||
private static class InvalidationTestData {
|
||||
private final DistributionSet distributionSet;
|
||||
private final List<Target> targets;
|
||||
private final TargetFilterQuery targetFilterQuery;
|
||||
private final Rollout rollout;
|
||||
|
||||
public InvalidationTestData(final DistributionSet distributionSet, final List<Target> targets,
|
||||
final TargetFilterQuery targetFilterQuery, final Rollout rollout) {
|
||||
super();
|
||||
this.distributionSet = distributionSet;
|
||||
this.targets = targets;
|
||||
this.targetFilterQuery = targetFilterQuery;
|
||||
this.rollout = rollout;
|
||||
}
|
||||
|
||||
public DistributionSet getDistributionSet() {
|
||||
return distributionSet;
|
||||
}
|
||||
|
||||
public List<Target> getTargets() {
|
||||
return targets;
|
||||
}
|
||||
|
||||
public TargetFilterQuery getTargetFilterQuery() {
|
||||
return targetFilterQuery;
|
||||
}
|
||||
|
||||
public Rollout getRollout() {
|
||||
return rollout;
|
||||
}
|
||||
}
|
||||
|
||||
private void assertDistributionSetInvalidationCount(
|
||||
final DistributionSetInvalidationCount distributionSetInvalidationCount,
|
||||
final long expectedAutoAssignmentCount, final long expectedActionCount, final long expectedRolloutCount) {
|
||||
assertThat(distributionSetInvalidationCount.getAutoAssignmentCount()).isEqualTo(expectedAutoAssignmentCount);
|
||||
assertThat(distributionSetInvalidationCount.getActionCount()).isEqualTo(expectedActionCount);
|
||||
assertThat(distributionSetInvalidationCount.getRolloutsCount()).isEqualTo(expectedRolloutCount);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -31,16 +31,20 @@ import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreated
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTagCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.UnsupportedSoftwareModuleForThisDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetFilter.DistributionSetFilterBuilder;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
@@ -174,6 +178,13 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
verifyThrownExceptionBy(() -> distributionSetManagement.updateMetaData(set.getId(),
|
||||
entityFactory.generateDsMetadata(NOT_EXIST_ID, "xxx")), "DistributionSetMetadata");
|
||||
|
||||
verifyThrownExceptionBy(() -> distributionSetManagement.getOrElseThrowException(NOT_EXIST_IDL),
|
||||
"DistributionSet");
|
||||
|
||||
verifyThrownExceptionBy(() -> distributionSetManagement.getValidAndComplete(NOT_EXIST_IDL), "DistributionSet");
|
||||
|
||||
verifyThrownExceptionBy(() -> distributionSetManagement.getValid(NOT_EXIST_IDL), "DistributionSet");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -493,6 +504,16 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(ds.isRequiredMigrationStep()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that an exception is thrown when trying to update an invalid distribution set")
|
||||
public void updateInvalidDistributionSet() {
|
||||
final DistributionSet distributionSet = testdataFactory.createAndInvalidateDistributionSet();
|
||||
|
||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||
.as("Invalid distributionSet should throw an exception").isThrownBy(() -> distributionSetManagement
|
||||
.update(entityFactory.distributionSet().update(distributionSet.getId()).name("new_name")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies the enforcement of the software module quota per distribution set.")
|
||||
public void assignSoftwareModulesUntilQuotaIsExceeded() {
|
||||
@@ -533,6 +554,33 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that an exception is thrown when trying to assign software modules to an invalidated distribution set.")
|
||||
public void verifyAssignSoftwareModulesToInvalidDistributionSet() {
|
||||
final DistributionSet distributionSet = testdataFactory.createAndInvalidateDistributionSet();
|
||||
final SoftwareModule softwareModule = testdataFactory.createSoftwareModuleOs();
|
||||
|
||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||
.as("Invalid distributionSet should throw an exception")
|
||||
.isThrownBy(() -> distributionSetManagement.assignSoftwareModules(distributionSet.getId(),
|
||||
Collections.singletonList(softwareModule.getId())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that an exception is thrown when trying to unassign a software module from an invalidated distribution set.")
|
||||
public void verifyUnassignSoftwareModulesToInvalidDistributionSet() {
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
final SoftwareModule softwareModule = testdataFactory.createSoftwareModuleOs();
|
||||
distributionSetManagement.assignSoftwareModules(distributionSet.getId(),
|
||||
Collections.singletonList(softwareModule.getId()));
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(new DistributionSetInvalidation(
|
||||
Collections.singletonList(distributionSet.getId()), CancelationType.NONE, false));
|
||||
|
||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||
.as("Invalid distributionSet should throw an exception").isThrownBy(() -> distributionSetManagement
|
||||
.unassignSoftwareModule(distributionSet.getId(), softwareModule.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithUser(allSpPermissions = true)
|
||||
@Description("Checks that metadata for a distribution set can be updated.")
|
||||
@@ -937,6 +985,24 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(distributionSetManagement.findByCompleted(PAGE, true).getTotalElements()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Deletes an invalid distribution set")
|
||||
public void deleteInvalidDistributionSet() {
|
||||
final DistributionSet set = testdataFactory.createAndInvalidateDistributionSet();
|
||||
assertThat(distributionSetRepository.findById(set.getId())).isNotEmpty();
|
||||
distributionSetManagement.delete(set.getId());
|
||||
assertThat(distributionSetRepository.findById(set.getId())).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Deletes an incomplete distribution set")
|
||||
public void deleteIncompleteDistributionSet() {
|
||||
final DistributionSet set = testdataFactory.createIncompleteDistributionSet();
|
||||
assertThat(distributionSetRepository.findById(set.getId())).isNotEmpty();
|
||||
distributionSetManagement.delete(set.getId());
|
||||
assertThat(distributionSetRepository.findById(set.getId())).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Queries and loads the metadata related to a given software module.")
|
||||
public void findAllDistributionSetMetadataByDsId() {
|
||||
@@ -1013,4 +1079,54 @@ public class DistributionSetManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(collect).containsAll(searchIds);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that an exception is thrown when trying to get an invalid distribution set")
|
||||
public void verifyGetValid() {
|
||||
final DistributionSet distributionSet = testdataFactory.createAndInvalidateDistributionSet();
|
||||
|
||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||
.as("Invalid distributionSet should throw an exception")
|
||||
.isThrownBy(() -> distributionSetManagement.getValid(distributionSet.getId()));
|
||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||
.as("Invalid distributionSet should throw an exception")
|
||||
.isThrownBy(() -> distributionSetManagement.getValidAndComplete(distributionSet.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that an exception is thrown when trying to get an incomplete distribution set")
|
||||
public void verifyGetValidAndComplete() {
|
||||
final DistributionSet distributionSet = testdataFactory.createIncompleteDistributionSet();
|
||||
|
||||
assertThatExceptionOfType(IncompleteDistributionSetException.class)
|
||||
.as("Incomplete distributionSet should throw an exception")
|
||||
.isThrownBy(() -> distributionSetManagement.getValidAndComplete(distributionSet.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify that an exception is thrown when trying to create or update metadata for an invalid distribution set.")
|
||||
public void createMetadataForInvalidDistributionSet() {
|
||||
final String knownKey1 = "myKnownKey1";
|
||||
final String knownKey2 = "myKnownKey2";
|
||||
final String knownValue = "myKnownValue";
|
||||
final String knownUpdateValue = "knownUpdateValue";
|
||||
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||
distributionSetManagement.createMetaData(ds.getId(),
|
||||
Collections.singletonList(entityFactory.generateDsMetadata(knownKey1, knownValue)));
|
||||
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(
|
||||
new DistributionSetInvalidation(Arrays.asList(ds.getId()), CancelationType.NONE, false));
|
||||
|
||||
// assert that no new metadata can be created
|
||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||
.as("Invalid distributionSet should throw an exception")
|
||||
.isThrownBy(() -> distributionSetManagement.createMetaData(ds.getId(),
|
||||
Collections.singletonList(entityFactory.generateDsMetadata(knownKey2, knownValue))));
|
||||
|
||||
// assert that an existing metadata can not be updated
|
||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||
.as("Invalid distributionSet should throw an exception").isThrownBy(() -> distributionSetManagement
|
||||
.updateMetaData(ds.getId(), entityFactory.generateDsMetadata(knownKey1, knownUpdateValue)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
|
||||
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
@@ -72,7 +74,6 @@ import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.Expect;
|
||||
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -243,8 +244,9 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(firstGroup.getStatus()).isEqualTo(RolloutGroupStatus.RUNNING);
|
||||
|
||||
// verify other groups are scheduled
|
||||
final List<RolloutGroup> scheduledGroups = rolloutGroupManagement.findByRollout(
|
||||
new OffsetBasedPageRequest(1, 100, Sort.by(Direction.ASC, "id")), createdRollout.getId()).getContent();
|
||||
final List<RolloutGroup> scheduledGroups = rolloutGroupManagement
|
||||
.findByRollout(new OffsetBasedPageRequest(1, 100, Sort.by(Direction.ASC, "id")), createdRollout.getId())
|
||||
.getContent();
|
||||
scheduledGroups.forEach(group -> assertThat(group.getStatus()).isEqualTo(RolloutGroupStatus.SCHEDULED)
|
||||
.as("group which should be in scheduled state is in " + group.getStatus() + " state"));
|
||||
// verify that the first group actions has been started and are in state
|
||||
@@ -436,8 +438,9 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(errorGroup.get(0).getStatus()).isEqualTo(RolloutGroupStatus.ERROR);
|
||||
|
||||
// all other groups should still be in scheduled state
|
||||
final List<RolloutGroup> scheduleGroups = rolloutGroupManagement.findByRollout(
|
||||
new OffsetBasedPageRequest(1, 100, Sort.by(Direction.ASC, "id")), createdRollout.getId()).getContent();
|
||||
final List<RolloutGroup> scheduleGroups = rolloutGroupManagement
|
||||
.findByRollout(new OffsetBasedPageRequest(1, 100, Sort.by(Direction.ASC, "id")), createdRollout.getId())
|
||||
.getContent();
|
||||
scheduleGroups.forEach(group -> assertThat(group.getStatus()).isEqualTo(RolloutGroupStatus.SCHEDULED));
|
||||
}
|
||||
|
||||
@@ -470,8 +473,9 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(rollout.getStatus()).isEqualTo(RolloutStatus.PAUSED);
|
||||
|
||||
// all other groups should still be in scheduled state
|
||||
final List<RolloutGroup> scheduleGroups = rolloutGroupManagement.findByRollout(
|
||||
new OffsetBasedPageRequest(1, 100, Sort.by(Direction.ASC, "id")), createdRollout.getId()).getContent();
|
||||
final List<RolloutGroup> scheduleGroups = rolloutGroupManagement
|
||||
.findByRollout(new OffsetBasedPageRequest(1, 100, Sort.by(Direction.ASC, "id")), createdRollout.getId())
|
||||
.getContent();
|
||||
scheduleGroups.forEach(group -> assertThat(group.getStatus()).isEqualTo(RolloutGroupStatus.SCHEDULED));
|
||||
|
||||
// resume the rollout again after it gets paused by error action
|
||||
@@ -521,8 +525,7 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
// verify all groups are in finished state
|
||||
rolloutGroupManagement
|
||||
.findByRollout(new OffsetBasedPageRequest(0, 100, Sort.by(Direction.ASC, "id")),
|
||||
createdRollout.getId())
|
||||
.findByRollout(new OffsetBasedPageRequest(0, 100, Sort.by(Direction.ASC, "id")), createdRollout.getId())
|
||||
.forEach(group -> assertThat(group.getStatus()).isEqualTo(RolloutGroupStatus.FINISHED));
|
||||
|
||||
// verify that rollout itself is in finished state
|
||||
@@ -1823,6 +1826,56 @@ public class RolloutManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(actions).allMatch(action -> !action.getWeight().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that an exception is thrown when trying to create a rollout with an invalidated distribution set.")
|
||||
public void createRolloutWithInvalidDistributionSet() {
|
||||
final DistributionSet distributionSet = testdataFactory.createAndInvalidateDistributionSet();
|
||||
|
||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||
.as("Invalid distributionSet should throw an exception")
|
||||
.isThrownBy(() -> testdataFactory.createRolloutByVariables("createRolloutWithInvalidDistributionSet",
|
||||
"desc", 2, "name==*", distributionSet, "50", "80"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that an exception is thrown when trying to create a rollout with an incomplete distribution set.")
|
||||
public void createRolloutWithIncompleteDistributionSet() {
|
||||
final DistributionSet distributionSet = testdataFactory.createIncompleteDistributionSet();
|
||||
|
||||
assertThatExceptionOfType(IncompleteDistributionSetException.class)
|
||||
.as("Incomplete distributionSet should throw an exception")
|
||||
.isThrownBy(() -> testdataFactory.createRolloutByVariables("createRolloutWithIncompleteDistributionSet",
|
||||
"desc", 2, "name==*", distributionSet, "50", "80"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that an exception is thrown when trying to update a rollout with an invalidated distribution set.")
|
||||
public void updateRolloutWithInvalidDistributionSet() {
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
testdataFactory.createTarget();
|
||||
final Rollout rollout = testdataFactory.createRolloutByVariables("updateRolloutWithInvalidDistributionSet",
|
||||
"desc", 2, "name==*", distributionSet, "50", "80");
|
||||
final DistributionSet invalidDistributionSet = testdataFactory.createAndInvalidateDistributionSet();
|
||||
|
||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||
.as("Invalid distributionSet should throw an exception").isThrownBy(() -> rolloutManagement
|
||||
.update(entityFactory.rollout().update(rollout.getId()).set(invalidDistributionSet.getId())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that an exception is thrown when trying to update a rollout with an incomplete distribution set.")
|
||||
public void updateRolloutWithIncompleteDistributionSet() {
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
testdataFactory.createTarget();
|
||||
final Rollout rollout = testdataFactory.createRolloutByVariables("updateRolloutWithIncompleteDistributionSet",
|
||||
"desc", 2, "name==*", distributionSet, "50", "80");
|
||||
final DistributionSet incompleteDistributionSet = testdataFactory.createIncompleteDistributionSet();
|
||||
|
||||
assertThatExceptionOfType(IncompleteDistributionSetException.class)
|
||||
.as("Incomplete distributionSet should throw an exception").isThrownBy(() -> rolloutManagement.update(
|
||||
entityFactory.rollout().update(rollout.getId()).set(incompleteDistributionSet.getId())));
|
||||
}
|
||||
|
||||
private RolloutGroupCreate generateRolloutGroup(final int index, final Integer percentage,
|
||||
final String targetFilter) {
|
||||
return entityFactory.rolloutGroup().create().name("Group" + index).description("Group" + index + "desc")
|
||||
|
||||
@@ -23,14 +23,17 @@ import javax.validation.ConstraintViolationException;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.builder.AutoAssignDistributionSetUpdate;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.SoftwareModuleCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetFilterQueryCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
@@ -252,7 +255,7 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
|
||||
.create(entityFactory.distributionSet().create().name("incomplete").version("1")
|
||||
.type(testdataFactory.findOrCreateDefaultTestDsType()));
|
||||
|
||||
assertThatExceptionOfType(InvalidAutoAssignDistributionSetException.class)
|
||||
assertThatExceptionOfType(IncompleteDistributionSetException.class)
|
||||
.isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
|
||||
.updateAutoAssign(targetFilterQuery.getId()).ds(incompleteDistributionSet.getId())));
|
||||
}
|
||||
@@ -263,7 +266,7 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
|
||||
assignDistributionSet(softDeletedDs, testdataFactory.createTarget("forSoftDeletedDs"));
|
||||
distributionSetManagement.delete(softDeletedDs.getId());
|
||||
|
||||
assertThatExceptionOfType(InvalidAutoAssignDistributionSetException.class)
|
||||
assertThatExceptionOfType(EntityNotFoundException.class)
|
||||
.isThrownBy(() -> targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
|
||||
.updateAutoAssign(targetFilterQuery.getId()).ds(softDeletedDs.getId())));
|
||||
}
|
||||
@@ -502,4 +505,60 @@ public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest
|
||||
assertThat(targetFilterQueryManagement.get(filterId).get().getAutoAssignWeight().get())
|
||||
.isEqualTo(Action.WEIGHT_MIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that an exception is thrown when trying to create a target filter with an invalidated distribution set.")
|
||||
public void createTargetFilterWithInvalidDistributionSet() {
|
||||
final DistributionSet distributionSet = testdataFactory.createAndInvalidateDistributionSet();
|
||||
|
||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||
.as("Invalid distributionSet should throw an exception")
|
||||
.isThrownBy(() -> targetFilterQueryManagement.create(
|
||||
entityFactory.targetFilterQuery().create().name("createTargetFilterWithInvalidDistributionSet")
|
||||
.query("name==*").autoAssignDistributionSet(distributionSet)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that an exception is thrown when trying to create a target filter with an incomplete distribution set.")
|
||||
public void createTargetFilterWithIncompleteDistributionSet() {
|
||||
final DistributionSet distributionSet = testdataFactory.createIncompleteDistributionSet();
|
||||
|
||||
assertThatExceptionOfType(IncompleteDistributionSetException.class)
|
||||
.as("Incomplete distributionSet should throw an exception")
|
||||
.isThrownBy(() -> targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create()
|
||||
.name("createTargetFilterWithIncompleteDistributionSet").query("name==*")
|
||||
.autoAssignDistributionSet(distributionSet)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that an exception is thrown when trying to update a target filter with an invalidated distribution set.")
|
||||
public void updateAutoAssignDsWithInvalidDistributionSet() {
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||
.create(entityFactory.targetFilterQuery().create().name("updateAutoAssignDsWithInvalidDistributionSet")
|
||||
.query("name==*").autoAssignDistributionSet(distributionSet));
|
||||
final DistributionSet invalidDistributionSet = testdataFactory.createAndInvalidateDistributionSet();
|
||||
|
||||
assertThatExceptionOfType(InvalidDistributionSetException.class)
|
||||
.as("Invalid distributionSet should throw an exception")
|
||||
.isThrownBy(() -> targetFilterQueryManagement
|
||||
.updateAutoAssignDS(new AutoAssignDistributionSetUpdate(targetFilterQuery.getId())
|
||||
.ds(invalidDistributionSet.getId())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies that an exception is thrown when trying to update a target filter with an incomplete distribution set.")
|
||||
public void updateAutoAssignDsWithIncompleteDistributionSet() {
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement.create(
|
||||
entityFactory.targetFilterQuery().create().name("updateAutoAssignDsWithIncompleteDistributionSet")
|
||||
.query("name==*").autoAssignDistributionSet(distributionSet));
|
||||
final DistributionSet incompleteDistributionSet = testdataFactory.createIncompleteDistributionSet();
|
||||
|
||||
assertThatExceptionOfType(IncompleteDistributionSetException.class)
|
||||
.as("Incomplete distributionSet should throw an exception")
|
||||
.isThrownBy(() -> targetFilterQueryManagement
|
||||
.updateAutoAssignDS(new AutoAssignDistributionSetUpdate(targetFilterQuery.getId())
|
||||
.ds(incompleteDistributionSet.getId())));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.jpa.ActionRepository;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
@@ -105,10 +105,10 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
|
||||
final DistributionSet setB = testdataFactory.createDistributionSet("dsB");
|
||||
|
||||
// target filter query that matches all targets
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement.updateAutoAssignDS(
|
||||
entityFactory.targetFilterQuery()
|
||||
.updateAutoAssign(targetFilterQueryManagement.create(
|
||||
entityFactory.targetFilterQuery().create().name("filterA").query("name==*")).getId())
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement.updateAutoAssignDS(entityFactory
|
||||
.targetFilterQuery()
|
||||
.updateAutoAssign(targetFilterQueryManagement
|
||||
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*")).getId())
|
||||
.ds(setA.getId()));
|
||||
|
||||
final String targetDsAIdPref = "targ";
|
||||
@@ -159,7 +159,7 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
// target filter query that matches first bunch of targets, that should
|
||||
// fail
|
||||
assertThatExceptionOfType(InvalidAutoAssignDistributionSetException.class).isThrownBy(() -> {
|
||||
assertThatExceptionOfType(IncompleteDistributionSetException.class).isThrownBy(() -> {
|
||||
final Long filterId = targetFilterQueryManagement.create(
|
||||
entityFactory.targetFilterQuery().create().name("filterA").query("id==" + targetDsFIdPref + "*"))
|
||||
.getId();
|
||||
@@ -221,10 +221,10 @@ public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
|
||||
final DistributionSet distributionSet, final List<Target> targets) {
|
||||
final Set<String> targetIds = targets.stream().map(Target::getControllerId).collect(Collectors.toSet());
|
||||
|
||||
actionRepository.findByDistributionSetId(Pageable.unpaged(), distributionSet.getId())
|
||||
.stream().filter(a -> targetIds.contains(a.getTarget().getControllerId()))
|
||||
.forEach(a -> assertThat(a.getInitiatedBy()).as(
|
||||
"Action should be initiated by the user who initiated the auto assignment")
|
||||
actionRepository.findByDistributionSetId(Pageable.unpaged(), distributionSet.getId()).stream()
|
||||
.filter(a -> targetIds.contains(a.getTarget().getControllerId()))
|
||||
.forEach(a -> assertThat(a.getInitiatedBy())
|
||||
.as("Action should be initiated by the user who initiated the auto assignment")
|
||||
.isEqualTo(targetFilterQuery.getAutoAssignInitiatedBy()));
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.eclipse.hawkbit.cache.TenantAwareCacheManager;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetInvalidationManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
|
||||
@@ -159,6 +160,9 @@ public abstract class AbstractIntegrationTest {
|
||||
@Autowired
|
||||
protected DeploymentManagement deploymentManagement;
|
||||
|
||||
@Autowired
|
||||
protected DistributionSetInvalidationManagement distributionSetInvalidationManagement;
|
||||
|
||||
@Autowired
|
||||
protected ArtifactManagement artifactManagement;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.Constants;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetInvalidationManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
|
||||
@@ -48,6 +49,8 @@ import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.ArtifactUpload;
|
||||
import org.eclipse.hawkbit.repository.model.BaseEntity;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
@@ -140,6 +143,9 @@ public class TestdataFactory {
|
||||
@Autowired
|
||||
private DistributionSetManagement distributionSetManagement;
|
||||
|
||||
@Autowired
|
||||
private DistributionSetInvalidationManagement distributionSetInvalidationManagement;
|
||||
|
||||
@Autowired
|
||||
private DistributionSetTypeManagement distributionSetTypeManagement;
|
||||
|
||||
@@ -656,8 +662,8 @@ public class TestdataFactory {
|
||||
* @return persisted {@link Target}
|
||||
*/
|
||||
public Target createTarget(final String controllerId, final String targetName, final Long targetTypeId) {
|
||||
final Target target = targetManagement
|
||||
.create(entityFactory.target().create().controllerId(controllerId).name(targetName).targetType(targetTypeId));
|
||||
final Target target = targetManagement.create(
|
||||
entityFactory.target().create().controllerId(controllerId).name(targetName).targetType(targetTypeId));
|
||||
assertTargetProperlyCreated(target);
|
||||
return target;
|
||||
}
|
||||
@@ -780,9 +786,9 @@ public class TestdataFactory {
|
||||
* @return persisted {@link SoftwareModuleType}
|
||||
*/
|
||||
public SoftwareModuleType findOrCreateSoftwareModuleType(final String key, final int maxAssignments) {
|
||||
return softwareModuleTypeManagement.getByKey(key)
|
||||
.orElseGet(() -> softwareModuleTypeManagement.create(entityFactory.softwareModuleType().create()
|
||||
.key(key).name(key).description(LOREM.words(10)).colour("#ffffff").maxAssignments(maxAssignments)));
|
||||
return softwareModuleTypeManagement.getByKey(key).orElseGet(
|
||||
() -> softwareModuleTypeManagement.create(entityFactory.softwareModuleType().create().key(key).name(key)
|
||||
.description(LOREM.words(10)).colour("#ffffff").maxAssignments(maxAssignments)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1187,8 +1193,8 @@ public class TestdataFactory {
|
||||
|
||||
/**
|
||||
* Finds {@link TargetType} in repository with given
|
||||
* {@link TargetType#getName()} or creates if it does not exist yet.
|
||||
* No ds types are assigned on creation.
|
||||
* {@link TargetType#getName()} or creates if it does not exist yet. No ds
|
||||
* types are assigned on creation.
|
||||
*
|
||||
* @param targetTypeName
|
||||
* {@link TargetType#getName()}
|
||||
@@ -1197,22 +1203,23 @@ public class TestdataFactory {
|
||||
*/
|
||||
public TargetType findOrCreateTargetType(final String targetTypeName) {
|
||||
return targetTypeManagement.getByName(targetTypeName)
|
||||
.orElseGet(() -> targetTypeManagement.create(entityFactory.targetType().create()
|
||||
.name(targetTypeName).description(targetTypeName + " description").colour(DEFAULT_COLOUR)));
|
||||
.orElseGet(() -> targetTypeManagement.create(entityFactory.targetType().create().name(targetTypeName)
|
||||
.description(targetTypeName + " description").colour(DEFAULT_COLOUR)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link TargetType} in repository with given
|
||||
* {@link TargetType#getName()}. Compatible distribution set types are assigned on creation
|
||||
* {@link TargetType#getName()}. Compatible distribution set types are
|
||||
* assigned on creation
|
||||
*
|
||||
* @param targetTypeName
|
||||
* {@link TargetType#getName()}
|
||||
*
|
||||
* @return persisted {@link TargetType}
|
||||
*/
|
||||
public TargetType createTargetType(final String targetTypeName, List<DistributionSetType> compatibleDsTypes) {
|
||||
return targetTypeManagement.create(entityFactory.targetType().create()
|
||||
.name(targetTypeName).description(targetTypeName + " description").colour(DEFAULT_COLOUR)
|
||||
public TargetType createTargetType(final String targetTypeName, final List<DistributionSetType> compatibleDsTypes) {
|
||||
return targetTypeManagement.create(entityFactory.targetType().create().name(targetTypeName)
|
||||
.description(targetTypeName + " description").colour(DEFAULT_COLOUR)
|
||||
.compatible(compatibleDsTypes.stream().map(DistributionSetType::getId).collect(Collectors.toList())));
|
||||
}
|
||||
|
||||
@@ -1225,13 +1232,37 @@ public class TestdataFactory {
|
||||
*
|
||||
* @return persisted {@link TargetType}
|
||||
*/
|
||||
public List<TargetType> createTargetTypes(final String targetTypePrefix, int count) {
|
||||
public List<TargetType> createTargetTypes(final String targetTypePrefix, final int count) {
|
||||
final List<TargetTypeCreate> result = Lists.newArrayListWithExpectedSize(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
result.add(entityFactory.targetType().create().name(targetTypePrefix + i).description(targetTypePrefix + " description")
|
||||
.colour(DEFAULT_COLOUR));
|
||||
result.add(entityFactory.targetType().create().name(targetTypePrefix + i)
|
||||
.description(targetTypePrefix + " description").colour(DEFAULT_COLOUR));
|
||||
}
|
||||
return targetTypeManagement.create(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a distribution set and directly invalidates it. No actions will
|
||||
* be canceled and no rollouts will be stopped with this invalidation.
|
||||
*
|
||||
* @return created invalidated {@link DistributionSet}
|
||||
*/
|
||||
public DistributionSet createAndInvalidateDistributionSet() {
|
||||
final DistributionSet distributionSet = createDistributionSet();
|
||||
distributionSetInvalidationManagement.invalidateDistributionSet(
|
||||
new DistributionSetInvalidation(Arrays.asList(distributionSet.getId()), CancelationType.NONE, false));
|
||||
return distributionSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a distribution set that has no software modules assigned, so it
|
||||
* is incomplete.
|
||||
*
|
||||
* @return created incomplete {@link DistributionSet}
|
||||
*/
|
||||
public DistributionSet createIncompleteDistributionSet() {
|
||||
return distributionSetManagement.create(entityFactory.distributionSet().create()
|
||||
.name(UUID.randomUUID().toString()).version(DEFAULT_VERSION).description(LOREM.words(10))
|
||||
.type(findOrCreateDefaultTestDsType()).requiredMigrationStep(false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO 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.mgmt.json.model.distributionset;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
|
||||
/**
|
||||
* Definition of the action cancel type for the distribution set invalidation
|
||||
* via REST management API.
|
||||
*
|
||||
*/
|
||||
public enum MgmtCancelationType {
|
||||
/**
|
||||
* Actions will be soft canceled.
|
||||
*/
|
||||
SOFT("soft"),
|
||||
|
||||
/**
|
||||
* Actions will be force quit.
|
||||
*/
|
||||
FORCE("force"),
|
||||
|
||||
/**
|
||||
* No actions will be canceled.
|
||||
*/
|
||||
NONE("none");
|
||||
|
||||
private final String name;
|
||||
|
||||
private MgmtCancelationType(final String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@JsonValue
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,17 @@ public class MgmtDistributionSet extends MgmtNamedEntity {
|
||||
@JsonProperty
|
||||
private boolean deleted;
|
||||
|
||||
@JsonProperty
|
||||
private boolean valid;
|
||||
|
||||
public boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
public void setValid(final boolean valid) {
|
||||
this.valid = valid;
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2021 Bosch.IO 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.mgmt.json.model.distributionset;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
/**
|
||||
* A json annotated rest model for invalidate DistributionSet requests.
|
||||
*
|
||||
*/
|
||||
public class MgmtInvalidateDistributionSetRequestBody {
|
||||
|
||||
@NotNull
|
||||
@JsonProperty
|
||||
private MgmtCancelationType actionCancelationType;
|
||||
@JsonProperty
|
||||
private boolean cancelRollouts;
|
||||
|
||||
public MgmtCancelationType getActionCancelationType() {
|
||||
return actionCancelationType;
|
||||
}
|
||||
|
||||
public void setActionCancelationType(final MgmtCancelationType actionCancelationType) {
|
||||
this.actionCancelationType = actionCancelationType;
|
||||
}
|
||||
|
||||
public boolean isCancelRollouts() {
|
||||
return cancelRollouts;
|
||||
}
|
||||
|
||||
public void setCancelRollouts(final boolean cancelRollouts) {
|
||||
this.cancelRollouts = cancelRollouts;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,12 +10,15 @@ package org.eclipse.hawkbit.mgmt.rest.api;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadataBodyPut;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSetRequestBodyPost;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSetRequestBodyPut;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtInvalidateDistributionSetRequestBody;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentRequestBody;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentResponseBody;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule;
|
||||
@@ -378,4 +381,19 @@ public interface MgmtDistributionSetRestApi {
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_OFFSET, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_OFFSET) int pagingOffsetParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_PAGING_LIMIT, defaultValue = MgmtRestConstants.REQUEST_PARAMETER_PAGING_DEFAULT_LIMIT) int pagingLimitParam,
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SORTING, required = false) String sortParam);
|
||||
|
||||
/**
|
||||
* Invalidates a distribution set
|
||||
*
|
||||
* @param distributionSetId
|
||||
* the ID of the distribution set to invalidate
|
||||
* @param invalidateRequestBody
|
||||
* the definition if rollouts and actions should be canceled
|
||||
* @return status OK if the invalidation was successful
|
||||
*/
|
||||
@PostMapping(value = "/{distributionSetId}/invalidate", consumes = { MediaTypes.HAL_JSON_VALUE,
|
||||
MediaType.APPLICATION_JSON_VALUE }, produces = { MediaTypes.HAL_JSON_VALUE,
|
||||
MediaType.APPLICATION_JSON_VALUE })
|
||||
ResponseEntity<Void> invalidateDistributionSet(@PathVariable("distributionSetId") Long distributionSetId,
|
||||
@Valid MgmtInvalidateDistributionSetRequestBody invalidateRequestBody);
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ public final class MgmtDistributionSetMapper {
|
||||
response.setComplete(distributionSet.isComplete());
|
||||
response.setType(distributionSet.getType().getKey());
|
||||
response.setDeleted(distributionSet.isDeleted());
|
||||
response.setValid(distributionSet.isValid());
|
||||
|
||||
distributionSet.getModules()
|
||||
.forEach(module -> response.getModules().add(MgmtSoftwareModuleMapper.toResponse(module)));
|
||||
|
||||
@@ -9,17 +9,21 @@
|
||||
package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadata;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtMetadataBodyPut;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.PagedList;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSetRequestBodyPost;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSetRequestBodyPut;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtInvalidateDistributionSetRequestBody;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentRequestBody;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtTargetAssignmentResponseBody;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.softwaremodule.MgmtSoftwareModule;
|
||||
@@ -29,6 +33,7 @@ import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtDistributionSetRestApi;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetInvalidationManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.EntityFactory;
|
||||
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
|
||||
@@ -40,6 +45,7 @@ import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
@@ -81,11 +87,14 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
|
||||
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
private final DistributionSetInvalidationManagement distributionSetInvalidationManagement;
|
||||
|
||||
MgmtDistributionSetResource(final SoftwareModuleManagement softwareModuleManagement,
|
||||
final TargetManagement targetManagement, final TargetFilterQueryManagement targetFilterQueryManagement,
|
||||
final DeploymentManagement deployManagament, final SystemManagement systemManagement,
|
||||
final EntityFactory entityFactory, final DistributionSetManagement distributionSetManagement,
|
||||
final SystemSecurityContext systemSecurityContext) {
|
||||
final SystemSecurityContext systemSecurityContext,
|
||||
final DistributionSetInvalidationManagement distributionSetInvalidationManagement) {
|
||||
this.softwareModuleManagement = softwareModuleManagement;
|
||||
this.targetManagement = targetManagement;
|
||||
this.targetFilterQueryManagement = targetFilterQueryManagement;
|
||||
@@ -94,6 +103,7 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
|
||||
this.entityFactory = entityFactory;
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.distributionSetInvalidationManagement = distributionSetInvalidationManagement;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -125,7 +135,7 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
|
||||
@Override
|
||||
public ResponseEntity<MgmtDistributionSet> getDistributionSet(
|
||||
@PathVariable("distributionSetId") final Long distributionSetId) {
|
||||
final DistributionSet foundDs = findDistributionSetWithExceptionIfNotFound(distributionSetId);
|
||||
final DistributionSet foundDs = distributionSetManagement.getOrElseThrowException(distributionSetId);
|
||||
|
||||
final MgmtDistributionSet response = MgmtDistributionSetMapper.toResponse(foundDs);
|
||||
MgmtDistributionSetMapper.addLinks(foundDs, response);
|
||||
@@ -206,7 +216,7 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
|
||||
@RequestParam(value = MgmtRestConstants.REQUEST_PARAMETER_SEARCH, required = false) final String rsqlParam) {
|
||||
// check if distribution set exists otherwise throw exception
|
||||
// immediately
|
||||
findDistributionSetWithExceptionIfNotFound(distributionSetId);
|
||||
distributionSetManagement.getOrElseThrowException(distributionSetId);
|
||||
|
||||
final int sanitizedOffsetParam = PagingUtility.sanitizeOffsetParam(pagingOffsetParam);
|
||||
final int sanitizedLimitParam = PagingUtility.sanitizePageLimitParam(pagingLimitParam);
|
||||
@@ -254,8 +264,7 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
|
||||
final List<Entry<String, Long>> offlineAssignments = assignments.stream()
|
||||
.map(assignment -> new SimpleEntry<String, Long>(assignment.getId(), distributionSetId))
|
||||
.collect(Collectors.toList());
|
||||
return ResponseEntity
|
||||
.ok(MgmtDistributionSetMapper
|
||||
return ResponseEntity.ok(MgmtDistributionSetMapper
|
||||
.toResponse(deployManagament.offlineAssignedDistributionSets(offlineAssignments)));
|
||||
}
|
||||
|
||||
@@ -374,8 +383,14 @@ public class MgmtDistributionSetResource implements MgmtDistributionSetRestApi {
|
||||
softwaremodules.getTotalElements()));
|
||||
}
|
||||
|
||||
private DistributionSet findDistributionSetWithExceptionIfNotFound(final Long distributionSetId) {
|
||||
return distributionSetManagement.get(distributionSetId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, distributionSetId));
|
||||
@Override
|
||||
public ResponseEntity<Void> invalidateDistributionSet(
|
||||
@PathVariable("distributionSetId") final Long distributionSetId,
|
||||
@Valid @RequestBody final MgmtInvalidateDistributionSetRequestBody invalidateRequestBody) {
|
||||
distributionSetInvalidationManagement
|
||||
.invalidateDistributionSet(new DistributionSetInvalidation(Arrays.asList(distributionSetId),
|
||||
MgmtRestModelMapper.convertCancelationType(invalidateRequestBody.getActionCancelationType()),
|
||||
invalidateRequestBody.isCancelRollouts()));
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ package org.eclipse.hawkbit.mgmt.rest.resource;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtBaseEntity;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.MgmtNamedEntity;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtCancelationType;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation.CancelationType;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
|
||||
@@ -100,4 +102,30 @@ public final class MgmtRestModelMapper {
|
||||
throw new IllegalStateException("Action Type is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given repository {@link CancelationType} into a
|
||||
* corresponding {@link MgmtCancelationType}.
|
||||
*
|
||||
* @param cancelationType
|
||||
* the repository representation of the cancellation type
|
||||
*
|
||||
* @return <null> or the REST cancellation type
|
||||
*/
|
||||
public static CancelationType convertCancelationType(final MgmtCancelationType cancelationType) {
|
||||
if (cancelationType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (cancelationType) {
|
||||
case SOFT:
|
||||
return CancelationType.SOFT;
|
||||
case FORCE:
|
||||
return CancelationType.FORCE;
|
||||
case NONE:
|
||||
return CancelationType.NONE;
|
||||
default:
|
||||
throw new IllegalStateException("Action Cancelation Type is not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,8 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
|
||||
// exception is thrown
|
||||
targetFilterQueryManagement.verifyTargetFilterQuerySyntax(rolloutRequestBody.getTargetFilterQuery());
|
||||
|
||||
final DistributionSet distributionSet = findDistributionSetOrThrowException(rolloutRequestBody);
|
||||
final DistributionSet distributionSet = distributionSetManagement
|
||||
.getValidAndComplete(rolloutRequestBody.getDistributionSetId());
|
||||
final RolloutGroupConditions rolloutGroupConditions = MgmtRolloutMapper.fromRequest(rolloutRequestBody, true);
|
||||
|
||||
final RolloutCreate create = MgmtRolloutMapper.fromRequest(entityFactory, rolloutRequestBody, distributionSet);
|
||||
@@ -234,10 +235,4 @@ public class MgmtRolloutResource implements MgmtRolloutRestApi {
|
||||
final List<MgmtTarget> rest = MgmtTargetMapper.toResponse(rolloutGroupTargets.getContent());
|
||||
return ResponseEntity.ok(new PagedList<>(rest, rolloutGroupTargets.getTotalElements()));
|
||||
}
|
||||
|
||||
private DistributionSet findDistributionSetOrThrowException(final MgmtRolloutRestRequestBody rolloutRequestBody) {
|
||||
return this.distributionSetManagement.get(rolloutRequestBody.getDistributionSetId()).orElseThrow(
|
||||
() -> new EntityNotFoundException(DistributionSet.class, rolloutRequestBody.getDistributionSetId()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,12 @@ import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetMetadata;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.test.util.TestdataFactory;
|
||||
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
||||
import org.eclipse.hawkbit.rest.util.JsonBuilder;
|
||||
@@ -1350,4 +1354,37 @@ public class MgmtDistributionSetResourceTest extends AbstractManagementApiIntegr
|
||||
assertThat(actions).size().isEqualTo(1);
|
||||
assertThat(actions.get(0).getWeight()).get().isEqualTo(weight);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verify invalidation of distribution sets that removes distribution sets from auto assignments, stops rollouts and cancels assignments")
|
||||
public void invalidateDistributionSet() throws Exception {
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
final List<Target> targets = testdataFactory.createTargets(5, "invalidateDistributionSet");
|
||||
assignDistributionSet(distributionSet, targets);
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||
.create(entityFactory.targetFilterQuery().create().name("invalidateDistributionSet").query("name==*")
|
||||
.autoAssignDistributionSet(distributionSet));
|
||||
final Rollout rollout = testdataFactory.createRolloutByVariables("invalidateDistributionSet", "desc", 2,
|
||||
"name==*", distributionSet, "50", "80");
|
||||
|
||||
final JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("actionCancelationType", "soft");
|
||||
jsonObject.put("cancelRollouts", true);
|
||||
|
||||
mvc.perform(post("/rest/v1/distributionsets/{ds}/invalidate", distributionSet.getId())
|
||||
.content(jsonObject.toString()).contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk());
|
||||
|
||||
assertThat(targetFilterQueryManagement.get(targetFilterQuery.getId()).get().getAutoAssignDistributionSet())
|
||||
.isNull();
|
||||
assertThat(rolloutManagement.get(rollout.getId()).get().getStatus()).isIn(RolloutStatus.STOPPING,
|
||||
RolloutStatus.FINISHED);
|
||||
for (final Target target : targets) {
|
||||
assertThat(targetManagement.get(target.getId()).get().getUpdateStatus())
|
||||
.isEqualTo(TargetUpdateStatus.PENDING);
|
||||
assertThat(deploymentManagement.findActionsByTarget(target.getControllerId(), PageRequest.of(0, 100))
|
||||
.getNumberOfElements()).isEqualTo(1);
|
||||
assertThat(deploymentManagement.findActionsByTarget(target.getControllerId(), PageRequest.of(0, 100))
|
||||
.getContent().get(0).getStatus()).isEqualTo(Status.CANCELING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,9 +25,10 @@ import java.util.List;
|
||||
import org.eclipse.hawkbit.exception.SpServerError;
|
||||
import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType;
|
||||
import org.eclipse.hawkbit.mgmt.rest.api.MgmtRestConstants;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
@@ -324,7 +325,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId() + "/autoAssignDS")
|
||||
.content("{\"id\":" + set.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(print()).andExpect(status().isForbidden())
|
||||
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(AssignmentQuotaExceededException.class.getName())))
|
||||
.andExpect(
|
||||
jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(AssignmentQuotaExceededException.class.getName())))
|
||||
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||
}
|
||||
|
||||
@@ -356,7 +358,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
mvc.perform(put(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + filterQuery.getId())
|
||||
.content("{\"query\":\"controllerId==target*\"}").contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(print()).andExpect(status().isForbidden())
|
||||
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(AssignmentQuotaExceededException.class.getName())))
|
||||
.andExpect(
|
||||
jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(AssignmentQuotaExceededException.class.getName())))
|
||||
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_QUOTA_EXCEEDED.getKey())));
|
||||
|
||||
}
|
||||
@@ -470,9 +473,8 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
.content("{\"id\":" + incompleteDistributionSet.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(print()).andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS,
|
||||
equalTo(InvalidAutoAssignDistributionSetException.class.getName())))
|
||||
.andExpect(jsonPath(JSON_PATH_ERROR_CODE,
|
||||
equalTo(SpServerError.SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID.getKey())));
|
||||
equalTo(IncompleteDistributionSetException.class.getName())))
|
||||
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_DS_INCOMPLETE.getKey())));
|
||||
}
|
||||
|
||||
@Step
|
||||
@@ -483,11 +485,9 @@ public class MgmtTargetFilterQueryResourceTest extends AbstractManagementApiInte
|
||||
|
||||
mvc.perform(post(MgmtRestConstants.TARGET_FILTER_V1_REQUEST_MAPPING + "/" + tfq.getId() + "/autoAssignDS")
|
||||
.content("{\"id\":" + softDeletedDs.getId() + "}").contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(print()).andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS,
|
||||
equalTo(InvalidAutoAssignDistributionSetException.class.getName())))
|
||||
.andExpect(jsonPath(JSON_PATH_ERROR_CODE,
|
||||
equalTo(SpServerError.SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID.getKey())));
|
||||
.andDo(print()).andExpect(status().isNotFound())
|
||||
.andExpect(jsonPath(JSON_PATH_EXCEPTION_CLASS, equalTo(EntityNotFoundException.class.getName())))
|
||||
.andExpect(jsonPath(JSON_PATH_ERROR_CODE, equalTo(SpServerError.SP_REPO_ENTITY_NOT_EXISTS.getKey())));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -83,11 +83,13 @@ public class ResponseExceptionHandler {
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_MAINTENANCE_SCHEDULE_INVALID, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_TARGET_ATTRIBUTES_INVALID, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_AUTO_ASSIGN_ACTION_TYPE_INVALID, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_AUTO_ASSIGN_DISTRIBUTION_SET_INVALID, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_CONFIGURATION_VALUE_CHANGE_NOT_ALLOWED, HttpStatus.FORBIDDEN);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_MULTIASSIGNMENT_NOT_ENABLED, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_NO_WEIGHT_PROVIDED_IN_MULTIASSIGNMENT_MODE, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_TARGET_TYPE_IN_USE, HttpStatus.CONFLICT);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_INVALID, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_DS_INCOMPLETE, HttpStatus.BAD_REQUEST);
|
||||
ERROR_TO_HTTP_STATUS.put(SpServerError.SP_STOP_ROLLOUT_FAILED, HttpStatus.LOCKED);
|
||||
}
|
||||
|
||||
private static HttpStatus getStatusOrDefault(final SpServerError error) {
|
||||
|
||||
@@ -830,6 +830,54 @@ include::../errors/429.adoc[]
|
||||
|===
|
||||
|
||||
|
||||
== POST /rest/v1/distributionsets/{distributionSetId}/invalidate
|
||||
|
||||
|
||||
=== Implementation Notes
|
||||
|
||||
Invalidate a distribution set. Once a distribution set is invalidated, it can not be valid again. An invalidated distribution set cannot be assigned to targets anymore. The distribution set that is going to be invalidated will be removed from all auto assignments. Furthermore, the user can choose to cancel all rollouts and (force) cancel all actions connected to this distribution set. Required permission: UPDATE_REPOSITORY
|
||||
|
||||
=== Invalidate a distribution set
|
||||
|
||||
==== Curl
|
||||
|
||||
include::{snippets}/distributionsets/invalidate/curl-request.adoc[]
|
||||
|
||||
==== Request URL
|
||||
|
||||
include::{snippets}/distributionsets/invalidate/http-request.adoc[]
|
||||
|
||||
==== Request path parameter
|
||||
|
||||
include::{snippets}/distributionsets/invalidate/path-parameters.adoc[]
|
||||
|
||||
==== Request fields
|
||||
|
||||
include::{snippets}/distributionsets/invalidate/request-fields.adoc[]
|
||||
|
||||
=== Response (Status 200)
|
||||
|
||||
==== Response example
|
||||
|
||||
include::{snippets}/distributionsets/invalidate/http-response.adoc[]
|
||||
|
||||
=== Error responses
|
||||
|
||||
|===
|
||||
| HTTP Status Code | Reason | Response Model
|
||||
|
||||
include::../errors/400.adoc[]
|
||||
include::../errors/401.adoc[]
|
||||
include::../errors/403.adoc[]
|
||||
include::../errors/404.adoc[]
|
||||
include::../errors/405.adoc[]
|
||||
include::../errors/406.adoc[]
|
||||
include::../errors/409.adoc[]
|
||||
include::../errors/415.adoc[]
|
||||
include::../errors/429.adoc[]
|
||||
|===
|
||||
|
||||
|
||||
== Additional content
|
||||
|
||||
[[error-body]]
|
||||
|
||||
@@ -89,8 +89,9 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
|
||||
protected String host = "management-api.host";
|
||||
|
||||
/**
|
||||
* The generated REST docs snippets will be outputted to an own resource folder.
|
||||
* The child class has to specify the name of that output folder where to put its corresponding snippets.
|
||||
* The generated REST docs snippets will be outputted to an own resource
|
||||
* folder. The child class has to specify the name of that output folder
|
||||
* where to put its corresponding snippets.
|
||||
*
|
||||
* @return the name of the resource folder
|
||||
*/
|
||||
@@ -101,8 +102,8 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
|
||||
this.document = document(getResourceName() + "/{method-name}", preprocessRequest(prettyPrint()),
|
||||
preprocessResponse(prettyPrint()));
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
|
||||
.apply(MockMvcRestDocumentation.documentationConfiguration(restDocContext).uris()
|
||||
.withScheme("https").withHost(host + ".com").withPort(443))
|
||||
.apply(MockMvcRestDocumentation.documentationConfiguration(restDocContext).uris().withScheme("https")
|
||||
.withHost(host + ".com").withPort(443))
|
||||
.alwaysDo(this.document).addFilter(filterHttpResponse).build();
|
||||
arrayPrefix = "[]";
|
||||
}
|
||||
@@ -163,17 +164,20 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
|
||||
|
||||
protected Target createTargetByGivenNameWithAttributes(final String name, final boolean inSync,
|
||||
final boolean timeforced, final DistributionSet distributionSet) {
|
||||
return createTargetByGivenNameWithAttributes(name, inSync, timeforced, distributionSet, null, null, null, false);
|
||||
return createTargetByGivenNameWithAttributes(name, inSync, timeforced, distributionSet, null, null, null,
|
||||
false);
|
||||
}
|
||||
|
||||
protected Target createTargetByGivenNameWithAttributes(final String name, final boolean inSync,
|
||||
final boolean timeforced, final DistributionSet distributionSet, final boolean createRollout) {
|
||||
return createTargetByGivenNameWithAttributes(name, inSync, timeforced, distributionSet, null, null, null, createRollout);
|
||||
return createTargetByGivenNameWithAttributes(name, inSync, timeforced, distributionSet, null, null, null,
|
||||
createRollout);
|
||||
}
|
||||
|
||||
protected Target createTargetByGivenNameWithAttributes(final String name, final boolean inSync,
|
||||
final boolean timeforced, final DistributionSet distributionSet, final String maintenanceWindowSchedule,
|
||||
final String maintenanceWindowDuration, final String maintenanceWindowTimeZone, final boolean createRollout) {
|
||||
final String maintenanceWindowDuration, final String maintenanceWindowTimeZone,
|
||||
final boolean createRollout) {
|
||||
|
||||
final Target savedTarget = targetManagement.create(entityFactory.target().create().controllerId(name)
|
||||
.status(TargetUpdateStatus.UNKNOWN).address("http://192.168.0.1").description("My name is " + name)
|
||||
@@ -318,6 +322,7 @@ public abstract class AbstractApiRestDocumentation extends AbstractRestIntegrati
|
||||
.description(MgmtApiModelProperties.DS_REQUIRED_STEP),
|
||||
fieldWithPath(arrayPrefix + "complete").description(MgmtApiModelProperties.DS_COMPLETE),
|
||||
fieldWithPath(arrayPrefix + "deleted").description(ApiModelPropertiesGeneric.DELETED),
|
||||
fieldWithPath(arrayPrefix + "valid").description(MgmtApiModelProperties.DS_VALID),
|
||||
fieldWithPath(arrayPrefix + "version").description(MgmtApiModelProperties.VERSION),
|
||||
fieldWithPath(arrayPrefix + "_links.self").ignored(), fieldWithPath(arrayPrefix + "modules").ignored());
|
||||
|
||||
|
||||
@@ -67,6 +67,8 @@ public final class MgmtApiModelProperties {
|
||||
public static final String DS_ALREADY_ASSIGNED_TARGETS = "Targets that had this distribution set already assigned (in \"offline\" case this includes targets that have arbitrary updates running)";
|
||||
public static final String DS_TOTAL_ASSIGNED_TARGETS = "Overall assigned as part of this request.";
|
||||
public static final String DS_ID = "Id of the distribution set.";
|
||||
public static final String DS_INVALIDATION_ACTION_CANCELATION_TYPE = "Type of cancelation for actions referring to the given distribution set.";
|
||||
public static final String DS_INVALIDATION_CANCEL_ROLLOUTS = "Defines if rollouts referring to this distribution set should be canceled.";
|
||||
|
||||
// Target
|
||||
public static final String INSTALLED_AT = "Installation time of current installed DistributionSet.";
|
||||
@@ -184,6 +186,8 @@ public final class MgmtApiModelProperties {
|
||||
|
||||
public static final String DS_COMPLETE = "True of the distribution set software module setup is complete as defined by the distribution set type.";
|
||||
|
||||
public static final String DS_VALID = "True by default and false after the distribution set is invalidated by the user.";
|
||||
|
||||
public static final String DS_TYPE_MANDATORY_MODULES = "Mandatory module type IDs.";
|
||||
|
||||
public static final String DS_TYPE_OPTIONAL_MODULES = "Optional module type IDs.";
|
||||
|
||||
@@ -379,8 +379,7 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
|
||||
parameterWithName("distributionSetId").description(ApiModelPropertiesGeneric.ITEM_ID)),
|
||||
requestParameters(parameterWithName("offline")
|
||||
.description(MgmtApiModelProperties.OFFLINE_UPDATE).optional()),
|
||||
requestFields(
|
||||
requestFieldWithPath("[].id").description(ApiModelPropertiesGeneric.ITEM_ID),
|
||||
requestFields(requestFieldWithPath("[].id").description(ApiModelPropertiesGeneric.ITEM_ID),
|
||||
requestFieldWithPathMandatoryInMultiAssignMode("[].weight")
|
||||
.description(MgmtApiModelProperties.ASSIGNMENT_WEIGHT)
|
||||
.type(JsonFieldType.NUMBER).attributes(key("value").value("0 - 1000")),
|
||||
@@ -395,8 +394,8 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
|
||||
optionalRequestFieldWithPath("[].maintenanceWindow.timezone")
|
||||
.description(MgmtApiModelProperties.MAINTENANCE_WINDOW_TIMEZONE),
|
||||
optionalRequestFieldWithPath("[].type")
|
||||
.description(MgmtApiModelProperties.ASSIGNMENT_TYPE)
|
||||
.attributes(key("value").value("['soft', 'forced','timeforced', 'downloadonly']"))),
|
||||
.description(MgmtApiModelProperties.ASSIGNMENT_TYPE).attributes(
|
||||
key("value").value("['soft', 'forced','timeforced', 'downloadonly']"))),
|
||||
responseFields(
|
||||
fieldWithPath("assigned").description(MgmtApiModelProperties.DS_NEW_ASSIGNED_TARGETS),
|
||||
fieldWithPath("alreadyAssigned").type(JsonFieldType.NUMBER)
|
||||
@@ -445,8 +444,7 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
|
||||
mockMvc.perform(delete(
|
||||
MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING
|
||||
+ "/{distributionSetId}/assignedSM/{softwareModuleId}",
|
||||
set.getId(), set.findFirstModuleByType(osType).get().getId())
|
||||
.contentType(MediaType.APPLICATION_JSON))
|
||||
set.getId(), set.findFirstModuleByType(osType).get().getId()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk())
|
||||
.andDo(this.document.document(pathParameters(
|
||||
parameterWithName("distributionSetId").description(ApiModelPropertiesGeneric.ITEM_ID),
|
||||
@@ -667,4 +665,28 @@ public class DistributionSetsDocumentationTest extends AbstractApiRestDocumentat
|
||||
optionalRequestFieldWithPath("[]value")
|
||||
.description(MgmtApiModelProperties.META_DATA_VALUE))));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Invalidates a distribution set. Required Permission: " + SpPermission.UPDATE_REPOSITORY)
|
||||
public void invalidate() throws Exception {
|
||||
final DistributionSet testDS = testdataFactory.createDistributionSet();
|
||||
|
||||
final JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("actionCancelationType", "soft");
|
||||
jsonObject.put("cancelRollouts", true);
|
||||
|
||||
mockMvc.perform(post(MgmtRestConstants.DISTRIBUTIONSET_V1_REQUEST_MAPPING + "/{distributionSetId}/invalidate",
|
||||
testDS.getId()).content(jsonObject.toString()).contentType(MediaType.APPLICATION_JSON))
|
||||
.andDo(MockMvcResultPrinter.print()).andExpect(status().isOk()).andDo(
|
||||
this.document.document(
|
||||
pathParameters(parameterWithName("distributionSetId")
|
||||
.description(ApiModelPropertiesGeneric.ITEM_ID)),
|
||||
requestFields(
|
||||
requestFieldWithPath("actionCancelationType")
|
||||
.description(
|
||||
MgmtApiModelProperties.DS_INVALIDATION_ACTION_CANCELATION_TYPE)
|
||||
.attributes(key("value").value("['force','soft','none']")),
|
||||
optionalRequestFieldWithPath("cancelRollouts")
|
||||
.description(MgmtApiModelProperties.DS_INVALIDATION_CANCEL_ROLLOUTS))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,6 +331,15 @@ public final class SpPermission {
|
||||
public static final String HAS_AUTH_UPDATE_REPOSITORY = HAS_AUTH_PREFIX + UPDATE_REPOSITORY + HAS_AUTH_SUFFIX
|
||||
+ HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||
|
||||
/**
|
||||
* Spring security eval hasAuthority expression to check if spring
|
||||
* context contains {@link SpPermission#READ_REPOSITORY} and
|
||||
* {@link SpPermission#UPDATE_REPOSITORY} or {@link #IS_SYSTEM_CODE}.
|
||||
*/
|
||||
public static final String HAS_AUTH_READ_REPOSITORY_AND_UPDATE_REPOSITORY = BRACKET_OPEN + HAS_AUTH_PREFIX
|
||||
+ READ_REPOSITORY + HAS_AUTH_SUFFIX + HAS_AUTH_AND + HAS_AUTH_PREFIX + UPDATE_REPOSITORY
|
||||
+ HAS_AUTH_SUFFIX + BRACKET_CLOSE + HAS_AUTH_OR + IS_SYSTEM_CODE;
|
||||
|
||||
/**
|
||||
* Spring security eval hasAuthority expression to check if spring
|
||||
* context contains {@link SpPermission#READ_REPOSITORY} and
|
||||
|
||||
Reference in New Issue
Block a user