Move rollout executor related target management methods in executor (#2812)
Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -9,6 +9,14 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.CREATE_ROLLOUT;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.DISTRIBUTION_SET;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.HANDLE_ROLLOUT;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.ROLLOUT;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_ROLLOUT;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_READ_REPOSITORY;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_UPDATE_REPOSITORY;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
@@ -50,7 +58,8 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
||||
*/
|
||||
public interface DeploymentManagement extends PermissionSupport {
|
||||
|
||||
String HAS_UPDATE_TARGET_AND_READ_DISTRIBUTION_SET = SpringEvalExpressions.HAS_UPDATE_REPOSITORY + " and hasAuthority('READ_" + SpPermission.DISTRIBUTION_SET + "')";
|
||||
String HAS_UPDATE_TARGET_AND_READ_DISTRIBUTION_SET = HAS_UPDATE_REPOSITORY + " and hasPermission(#root, 'READ_" + DISTRIBUTION_SET + "')";
|
||||
String HAS_UPDATE_TARGET_OR_CREATE_UPDATE_ROLLOUT = HAS_UPDATE_REPOSITORY + " or hasPermission(#root, '" + CREATE_ROLLOUT + "')" + " or hasPermission(#root, '" + UPDATE_ROLLOUT + "')";
|
||||
|
||||
@Override
|
||||
default String permissionGroup() {
|
||||
@@ -63,12 +72,12 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @param deploymentRequests information about all target-ds-assignments that shall be made
|
||||
* @return the list of assignment results
|
||||
* @throws IncompleteDistributionSetException if mandatory {@link SoftwareModuleType} are not assigned as
|
||||
* defined by the {@link DistributionSetType}.
|
||||
* defined by the {@link DistributionSetType}.
|
||||
* @throws EntityNotFoundException if either provided {@link DistributionSet} or {@link Target}s do not exist
|
||||
* @throws AssignmentQuotaExceededException if the maximum number of targets the distribution set can be
|
||||
* assigned to at once is exceeded
|
||||
* assigned to at once is exceeded
|
||||
* @throws MultiAssignmentIsNotEnabledException if the request results in multiple assignments to the same
|
||||
* target and multi-assignment is disabled
|
||||
* target and multi-assignment is disabled
|
||||
*/
|
||||
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_DISTRIBUTION_SET)
|
||||
List<DistributionSetAssignmentResult> assignDistributionSets(@Valid @NotEmpty List<DeploymentRequest> deploymentRequests);
|
||||
@@ -81,12 +90,12 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @param actionMessage an optional message for the action status
|
||||
* @return the list of assignment results
|
||||
* @throws IncompleteDistributionSetException if mandatory {@link SoftwareModuleType} are not assigned as
|
||||
* defined by the {@link DistributionSetType}.
|
||||
* defined by the {@link DistributionSetType}.
|
||||
* @throws EntityNotFoundException if either provided {@link DistributionSet} or {@link Target}s do not exist
|
||||
* @throws AssignmentQuotaExceededException if the maximum number of targets the distribution set can be
|
||||
* assigned to at once is exceeded
|
||||
* assigned to at once is exceeded
|
||||
* @throws MultiAssignmentIsNotEnabledException if the request results in multiple assignments to the same
|
||||
* target and multi-assignment is disabled
|
||||
* target and multi-assignment is disabled
|
||||
*/
|
||||
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_DISTRIBUTION_SET)
|
||||
List<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
@@ -108,11 +117,11 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @param assignments target IDs with the respective distribution set ID which they are supposed to be assigned to
|
||||
* @return the assignment results
|
||||
* @throws IncompleteDistributionSetException if mandatory {@link SoftwareModuleType} are not assigned as
|
||||
* defined by the {@link DistributionSetType}.
|
||||
* defined by the {@link DistributionSetType}.
|
||||
* @throws EntityNotFoundException if either provided {@link DistributionSet} or {@link Target}s do not exist
|
||||
* @throws AssignmentQuotaExceededException if the maximum number of targets the distribution set can be assigned to at once is exceeded
|
||||
* @throws MultiAssignmentIsNotEnabledException if the request results in multiple assignments to the same
|
||||
* target and multi-assignment is disabled
|
||||
* target and multi-assignment is disabled
|
||||
*/
|
||||
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_DISTRIBUTION_SET)
|
||||
List<DistributionSetAssignmentResult> offlineAssignedDistributionSets(String initiatedBy, Collection<Entry<String, Long>> assignments);
|
||||
@@ -129,7 +138,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @throws CancelActionNotAllowedException in case the given action is not active or is already a cancel action
|
||||
* @throws EntityNotFoundException if action with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
||||
@PreAuthorize(HAS_UPDATE_REPOSITORY)
|
||||
Action cancelAction(long actionId);
|
||||
|
||||
/**
|
||||
@@ -139,11 +148,11 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @param controllerId the target associated to the actions to count
|
||||
* @return the count value of found actions associated to the target
|
||||
* @throws RSQLParameterUnsupportedFieldException if a field in the RSQL string is used but not provided by the
|
||||
* given {@code fieldNameProvider}
|
||||
* given {@code fieldNameProvider}
|
||||
* @throws RSQLParameterSyntaxException if the RSQL syntax is wrong
|
||||
* @throws EntityNotFoundException if target with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
long countActionsByTarget(@NotNull String rsql, @NotEmpty String controllerId);
|
||||
|
||||
/**
|
||||
@@ -151,7 +160,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
*
|
||||
* @return the total amount of stored actions
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
long countActionsAll();
|
||||
|
||||
/**
|
||||
@@ -160,7 +169,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @param rsql RSQL query.
|
||||
* @return the total number of actions matching the given RSQL query.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
long countActions(@NotNull String rsql);
|
||||
|
||||
/**
|
||||
@@ -170,7 +179,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @return the count value of found actions associated to the target
|
||||
* @throws EntityNotFoundException if target with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
long countActionsByTarget(@NotEmpty String controllerId);
|
||||
|
||||
/**
|
||||
@@ -179,7 +188,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @param actionId to be id of the action
|
||||
* @return the corresponding {@link Action}
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
Optional<Action> findAction(long actionId);
|
||||
|
||||
/**
|
||||
@@ -188,7 +197,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @param pageable pagination parameter
|
||||
* @return a paged list of {@link Action}s
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
Slice<Action> findActionsAll(@NotNull Pageable pageable);
|
||||
|
||||
/**
|
||||
@@ -198,7 +207,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @param pageable the page request parameter for paging and sorting the result
|
||||
* @return a paged list of {@link Action}s.
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
Slice<Action> findActions(@NotNull String rsql, @NotNull Pageable pageable);
|
||||
|
||||
/**
|
||||
@@ -209,10 +218,10 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @param pageable the page request
|
||||
* @return a slice of actions assigned to the specific target and the specification
|
||||
* @throws RSQLParameterUnsupportedFieldException if a field in the RSQL string is used but not provided by the
|
||||
* given {@code fieldNameProvider}
|
||||
* given {@code fieldNameProvider}
|
||||
* @throws RSQLParameterSyntaxException if the RSQL syntax is wrong
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
Slice<Action> findActionsByTarget(@NotNull String rsql, @NotEmpty String controllerId, @NotNull Pageable pageable);
|
||||
|
||||
/**
|
||||
@@ -222,7 +231,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @param pageable the pageable request to limit, sort the actions
|
||||
* @return a slice of actions found for a specific target
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
Slice<Action> findActionsByTarget(@NotEmpty String controllerId, @NotNull Pageable pageable);
|
||||
|
||||
/**
|
||||
@@ -233,7 +242,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @return the corresponding {@link Page} of {@link ActionStatus}
|
||||
* @throws EntityNotFoundException if action with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
Page<ActionStatus> findActionStatusByAction(long actionId, @NotNull Pageable pageable);
|
||||
|
||||
/**
|
||||
@@ -243,7 +252,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @param pageable the page request parameter for paging and sorting the result
|
||||
* @return a page of messages by a specific {@link ActionStatus} id
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
Page<String> findMessagesByActionStatusId(long actionStatusId, @NotNull Pageable pageable);
|
||||
|
||||
/**
|
||||
@@ -252,7 +261,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @param actionId to be id of the action
|
||||
* @return the corresponding {@link Action}
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
Optional<Action> findActionWithDetails(long actionId);
|
||||
|
||||
/**
|
||||
@@ -263,7 +272,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @return a list of actions associated with the given target
|
||||
* @throws EntityNotFoundException if target with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
Page<Action> findActiveActionsByTarget(@NotEmpty String controllerId, @NotNull Pageable pageable);
|
||||
|
||||
/**
|
||||
@@ -273,7 +282,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @param maxActionCount max size of returned list
|
||||
* @return the action
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
List<Action> findActiveActionsWithHighestWeight(@NotEmpty String controllerId, int maxActionCount);
|
||||
|
||||
/**
|
||||
@@ -285,7 +294,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @throws CancelActionNotAllowedException in case the given action is not active
|
||||
* @throws EntityNotFoundException if action with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
||||
@PreAuthorize(HAS_UPDATE_REPOSITORY)
|
||||
Action forceQuitAction(long actionId);
|
||||
|
||||
/**
|
||||
@@ -295,11 +304,12 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @return the updated or the found {@link Action}
|
||||
* @throws EntityNotFoundException if action with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
||||
@PreAuthorize(HAS_UPDATE_REPOSITORY)
|
||||
Action forceTargetAction(long actionId);
|
||||
|
||||
/**
|
||||
* Deletes the current action by id.
|
||||
*
|
||||
* @param actionId - action id
|
||||
*/
|
||||
@PreAuthorize("hasAuthority('UPDATE_" + SpPermission.TARGET + "')")
|
||||
@@ -307,6 +317,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
|
||||
/**
|
||||
* Deletes actions matching the provided rsql filter
|
||||
*
|
||||
* @param rsql - rsql filter for actions to be deleted
|
||||
*/
|
||||
@PreAuthorize("hasAuthority('UPDATE_" + SpPermission.TARGET + "')")
|
||||
@@ -314,6 +325,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
|
||||
/**
|
||||
* Deletes actions present in provided list of ids
|
||||
*
|
||||
* @param actionIds - list of action ids to be deleted
|
||||
*/
|
||||
@PreAuthorize("hasAuthority('UPDATE_" + SpPermission.TARGET + "')")
|
||||
@@ -342,7 +354,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
*
|
||||
* @param targetIds ids of the {@link Target}s the actions belong to
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
||||
@PreAuthorize(HAS_UPDATE_TARGET_OR_CREATE_UPDATE_ROLLOUT)
|
||||
void cancelInactiveScheduledActionsForTargets(List<Long> targetIds);
|
||||
|
||||
/**
|
||||
@@ -352,7 +364,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @param distributionSetId to assign
|
||||
* @param rolloutGroupParentId the parent rollout group the actions should reference. null references the first group
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_UPDATE_TARGET_OR_CREATE_UPDATE_ROLLOUT)
|
||||
void startScheduledActionsByRolloutGroupParent(long rolloutId, long distributionSetId, Long rolloutGroupParentId);
|
||||
|
||||
/**
|
||||
@@ -360,7 +372,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
*
|
||||
* @param rolloutGroupActions rollouts group actions part of a same group
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_UPDATE_TARGET_OR_CREATE_UPDATE_ROLLOUT)
|
||||
void startScheduledActions(final List<Action> rolloutGroupActions);
|
||||
|
||||
/**
|
||||
@@ -370,7 +382,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @return assigned {@link DistributionSet}
|
||||
* @throws EntityNotFoundException if target with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
Optional<DistributionSet> findAssignedDistributionSet(@NotEmpty String controllerId);
|
||||
|
||||
/**
|
||||
@@ -380,7 +392,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @return installed {@link DistributionSet}
|
||||
* @throws EntityNotFoundException if target with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
Optional<DistributionSet> findInstalledDistributionSet(@NotEmpty String controllerId);
|
||||
|
||||
/**
|
||||
@@ -400,7 +412,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @param targetId of target
|
||||
* @return if actions in CANCELING state are present
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
boolean hasPendingCancellations(@NotNull Long targetId);
|
||||
|
||||
/**
|
||||
@@ -409,9 +421,9 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @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_UPDATE_REPOSITORY)
|
||||
@PreAuthorize(HAS_UPDATE_REPOSITORY)
|
||||
void cancelActionsForDistributionSet(final ActionCancellationType cancelationType, final DistributionSet set);
|
||||
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_UPDATE_REPOSITORY + " or " + SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
@PreAuthorize(HAS_UPDATE_REPOSITORY + " or " + SpringEvalExpressions.IS_SYSTEM_CODE)
|
||||
void handleMaxAssignmentsExceeded(Long targetId, Long requested, AssignmentQuotaExceededException quotaExceededException);
|
||||
}
|
||||
@@ -57,8 +57,8 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
||||
*/
|
||||
public interface RolloutManagement extends PermissionSupport {
|
||||
|
||||
String HAS_ROLLOUT_APPROVE = "hasPermission(#root, 'APPROVE')";
|
||||
String HAS_ROLLOUT_HANDLE = "hasPermission(#root, 'HANDLE')";
|
||||
String HAS_ROLLOUT_APPROVE = "hasPermission(#root, 'APPROVE_${permissionGroup}')";
|
||||
String HAS_ROLLOUT_HANDLE = "hasPermission(#root, 'HANDLE_${permissionGroup}')";
|
||||
|
||||
@Override
|
||||
default String permissionGroup() {
|
||||
|
||||
@@ -39,7 +39,6 @@ import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldExc
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
@@ -144,49 +143,6 @@ public interface TargetManagement<T extends Target>
|
||||
Slice<Target> findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(
|
||||
long distributionSetId, @NotNull String rsql, @NotNull Pageable pageable);
|
||||
|
||||
/**
|
||||
* Finds all targets for all the given parameter {@link TargetFilterQuery} and that are not assigned to one of the {@link RolloutGroup}s
|
||||
* and are compatible with the passed {@link DistributionSetType}.
|
||||
*
|
||||
* @param groups the list of {@link RolloutGroup}s
|
||||
* @param rsql filter definition in RSQL syntax
|
||||
* @param distributionSetType type of the {@link DistributionSet} the targets must be compatible withs
|
||||
* @param pageable the pageable to enhance the query for paging and sorting
|
||||
* @return a page of the found {@link Target}s
|
||||
*/
|
||||
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_ROLLOUT)
|
||||
Slice<Target> findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
|
||||
@NotEmpty Collection<Long> groups, @NotNull String rsql, @NotNull DistributionSetType distributionSetType,
|
||||
@NotNull Pageable pageable);
|
||||
|
||||
/**
|
||||
* Finds all targets with failed actions for specific Rollout and that are not assigned to one of the retried {@link RolloutGroup}s and
|
||||
* are compatible with the passed {@link DistributionSetType}.
|
||||
*
|
||||
* @param rolloutId rolloutId of the rollout to be retried.
|
||||
* @param groups the list of {@link RolloutGroup}s
|
||||
* @param pageable the pageable to enhance the query for paging and sorting
|
||||
* @return a page of the found {@link Target}s
|
||||
*/
|
||||
@PreAuthorize(HAS_READ_TARGET_AND_READ_ROLLOUT)
|
||||
Slice<Target> findByFailedRolloutAndNotInRolloutGroups(
|
||||
@NotNull String rolloutId, @NotEmpty Collection<Long> groups, @NotNull Pageable pageable);
|
||||
|
||||
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_ROLLOUT)
|
||||
Slice<Target> findByRsqlAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
|
||||
final long rolloutId, @NotNull String rsql, @NotNull DistributionSetType distributionSetType, @NotNull Pageable pageable);
|
||||
|
||||
/**
|
||||
* Finds all targets of the provided {@link RolloutGroup} that have no Action for the RolloutGroup.
|
||||
*
|
||||
* @param group the {@link RolloutGroup}
|
||||
* @param pageable the pageable to enhance the query for paging and sorting
|
||||
* @return the found {@link Target}s
|
||||
* @throws EntityNotFoundException if rollout group with given ID does not exist
|
||||
*/
|
||||
@PreAuthorize(HAS_READ_REPOSITORY)
|
||||
Slice<Target> findByInRolloutGroupWithoutAction(long group, @NotNull Pageable pageable);
|
||||
|
||||
/**
|
||||
* Retrieves {@link Target}s by the assigned {@link DistributionSet}.
|
||||
*
|
||||
@@ -299,33 +255,6 @@ public interface TargetManagement<T extends Target>
|
||||
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_DISTRIBUTION_SET)
|
||||
long countByRsqlAndNonDsAndCompatibleAndUpdatable(long distributionSetId, @NotNull String rsql);
|
||||
|
||||
/**
|
||||
* Counts all targets for all the given parameter {@link TargetFilterQuery} and that are not assigned to one of the {@link RolloutGroup}s
|
||||
* and are compatible with the passed {@link DistributionSetType}.
|
||||
*
|
||||
* @param rsql filter definition in RSQL syntax
|
||||
* @param groups the list of {@link RolloutGroup}s
|
||||
* @param distributionSetType type of the {@link DistributionSet} the targets must be compatible with
|
||||
* @return count of the found {@link Target}s
|
||||
*/
|
||||
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_ROLLOUT)
|
||||
long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
|
||||
@NotNull String rsql, @NotEmpty Collection<Long> groups, @NotNull DistributionSetType distributionSetType);
|
||||
|
||||
/**
|
||||
* Counts all targets with failed actions for specific Rollout and that are not assigned to one of the {@link RolloutGroup}s and are
|
||||
* compatible with the passed {@link DistributionSetType}.
|
||||
*
|
||||
* @param rolloutId rolloutId of the rollout to be retried.
|
||||
* @param groups the list of {@link RolloutGroup}s
|
||||
* @return count of the found {@link Target}s
|
||||
*/
|
||||
@PreAuthorize(HAS_READ_TARGET_AND_READ_ROLLOUT)
|
||||
long countByFailedRolloutAndNotInRolloutGroups(@NotNull String rolloutId, @NotEmpty Collection<Long> groups);
|
||||
|
||||
@PreAuthorize(HAS_READ_TARGET_AND_READ_ROLLOUT)
|
||||
long countByActionsInRolloutGroup(final long rolloutGroupId);
|
||||
|
||||
/**
|
||||
* Deletes target with the given controller ID.
|
||||
*
|
||||
|
||||
@@ -13,6 +13,7 @@ import java.util.Optional;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.im.authentication.Hierarchy;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -46,7 +47,8 @@ public class RepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@SuppressWarnings("java:S3358") // java:S3358 better readable this way
|
||||
@SuppressWarnings("java:S3358")
|
||||
// java:S3358 better readable this way
|
||||
RoleHierarchy roleHierarchy(
|
||||
// if configured replaces the hierarchy completely
|
||||
@Value("${hawkbit.hierarchy:}") final String hierarchy,
|
||||
@@ -66,15 +68,15 @@ public class RepositoryConfiguration {
|
||||
public boolean hasPermission(final Authentication authentication, final Object targetDomainObject, final Object permission) {
|
||||
if (targetDomainObject instanceof MethodSecurityExpressionOperations root &&
|
||||
root.getThis() instanceof PermissionSupport permissionSupport) {
|
||||
final String neededPermission = permission + "_" + permissionSupport.permissionGroup();
|
||||
final String neededPermission = String.valueOf(permission)
|
||||
.replace(SpringEvalExpressions.PERMISSION_GROUP_PLACEHOLDER, permissionSupport.permissionGroup());
|
||||
|
||||
// do permissions check
|
||||
final boolean hasPermission = roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities()).stream()
|
||||
.map(GrantedAuthority::getAuthority)
|
||||
.anyMatch(authority -> authority.equals(neededPermission));
|
||||
if (!hasPermission) {
|
||||
log.debug(
|
||||
"User {} does not have permission {} for target {}",
|
||||
log.debug("User {} does not have permission {} for target {}",
|
||||
authentication.getName(), neededPermission, targetDomainObject);
|
||||
}
|
||||
|
||||
|
||||
@@ -473,11 +473,11 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
final PageRequest pageRequest = PageRequest.of(0, ACTION_PAGE_LIMIT);
|
||||
final Page<Action> groupScheduledActions;
|
||||
if (rolloutGroupParentId == null) {
|
||||
groupScheduledActions = actionRepository.findByRolloutIdAndRolloutGroupParentIsNullAndStatus(pageRequest, rolloutId,
|
||||
Action.Status.SCHEDULED);
|
||||
groupScheduledActions = actionRepository.findByRolloutIdAndRolloutGroupParentIsNullAndStatus(
|
||||
pageRequest, rolloutId, Action.Status.SCHEDULED);
|
||||
} else {
|
||||
groupScheduledActions = actionRepository.findByRolloutIdAndRolloutGroupParentIdAndStatus(pageRequest, rolloutId,
|
||||
rolloutGroupParentId, Action.Status.SCHEDULED);
|
||||
groupScheduledActions = actionRepository.findByRolloutIdAndRolloutGroupParentIdAndStatus(
|
||||
pageRequest, rolloutId, rolloutGroupParentId, Action.Status.SCHEDULED);
|
||||
}
|
||||
|
||||
if (groupScheduledActions.getContent().isEmpty()) {
|
||||
@@ -493,8 +493,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
@Override
|
||||
public void startScheduledActions(final List<Action> rolloutGroupActions) {
|
||||
// Close actions already assigned and collect pending assignments
|
||||
final List<JpaAction> pendingTargetAssignments = rolloutGroupActions.stream().map(JpaAction.class::cast)
|
||||
.map(this::closeActionIfSetWasAlreadyAssigned).filter(Objects::nonNull).toList();
|
||||
final List<JpaAction> pendingTargetAssignments = rolloutGroupActions.stream()
|
||||
.map(JpaAction.class::cast).map(this::closeActionIfSetWasAlreadyAssigned).filter(Objects::nonNull).toList();
|
||||
if (pendingTargetAssignments.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -987,10 +987,9 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
final JpaTarget target = action.getTarget();
|
||||
if (target.getAssignedDistributionSet() != null && action.getDistributionSet().getId()
|
||||
.equals(target.getAssignedDistributionSet().getId())) {
|
||||
// the target has already the distribution set assigned, we don't
|
||||
// need to start the scheduled action, just finish it.
|
||||
log.debug("Target {} has distribution set {} assigned. Closing action...", target.getControllerId(),
|
||||
action.getDistributionSet().getName());
|
||||
// the target has already the distribution set assigned, we don't need to start the scheduled action, just finish it.
|
||||
log.debug("Target {} has distribution set {} assigned. Closing action...",
|
||||
target.getControllerId(), action.getDistributionSet().getName());
|
||||
action.setStatus(Status.FINISHED);
|
||||
action.setActive(false);
|
||||
setSkipActionStatus(action);
|
||||
@@ -1023,8 +1022,8 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
private List<JpaAction> activateActionsOfRolloutGroup(final List<JpaAction> actions) {
|
||||
actions.forEach(action -> {
|
||||
action.setActive(true);
|
||||
final boolean confirmationRequired = action.getRolloutGroup().isConfirmationRequired() && action.getTarget()
|
||||
.getAutoConfirmationStatus() == null;
|
||||
final boolean confirmationRequired = action.getRolloutGroup().isConfirmationRequired() &&
|
||||
action.getTarget().getAutoConfirmationStatus() == null;
|
||||
if (isConfirmationFlowEnabled() && confirmationRequired) {
|
||||
action.setStatus(Status.WAIT_FOR_CONFIRMATION);
|
||||
return;
|
||||
|
||||
@@ -46,15 +46,12 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetTypeRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetFields;
|
||||
@@ -84,7 +81,6 @@ public class JpaTargetManagement
|
||||
private final JpaDistributionSetManagement distributionSetManagement;
|
||||
private final QuotaManagement quotaManagement;
|
||||
private final TargetTypeRepository targetTypeRepository;
|
||||
private final RolloutGroupRepository rolloutGroupRepository;
|
||||
private final TargetTagRepository targetTagRepository;
|
||||
|
||||
@SuppressWarnings("java:S107")
|
||||
@@ -92,13 +88,11 @@ public class JpaTargetManagement
|
||||
final TargetRepository jpaRepository, final EntityManager entityManager,
|
||||
final JpaDistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
|
||||
final TargetTypeRepository targetTypeRepository,
|
||||
final RolloutGroupRepository rolloutGroupRepository,
|
||||
final TargetTagRepository targetTagRepository) {
|
||||
super(jpaRepository, entityManager);
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.quotaManagement = quotaManagement;
|
||||
this.targetTypeRepository = targetTypeRepository;
|
||||
this.rolloutGroupRepository = rolloutGroupRepository;
|
||||
this.targetTagRepository = targetTagRepository;
|
||||
}
|
||||
|
||||
@@ -162,50 +156,6 @@ public class JpaTargetManagement
|
||||
.map(Target.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Target> findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
|
||||
final Collection<Long> groups, final String rsql, final DistributionSetType dsType, final Pageable pageable) {
|
||||
return jpaRepository
|
||||
.findAllWithoutCount(AccessController.Operation.UPDATE,
|
||||
combineWithAnd(List.of(
|
||||
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
|
||||
TargetSpecifications.isNotInRolloutGroups(groups),
|
||||
TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))),
|
||||
pageable)
|
||||
.map(Target.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Target> findByFailedRolloutAndNotInRolloutGroups(String rolloutId, Collection<Long> groups, Pageable pageable) {
|
||||
final List<Specification<JpaTarget>> specList = List.of(
|
||||
TargetSpecifications.failedActionsForRollout(rolloutId),
|
||||
TargetSpecifications.isNotInRolloutGroups(groups));
|
||||
return JpaManagementHelper.findAllWithCountBySpec(jpaRepository, specList, pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Target> findByRsqlAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
|
||||
final long rolloutId, final String rsql, final DistributionSetType distributionSetType, final Pageable pageable) {
|
||||
return jpaRepository
|
||||
.findAllWithoutCount(AccessController.Operation.UPDATE,
|
||||
combineWithAnd(List.of(
|
||||
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
|
||||
TargetSpecifications.hasNoOverridingActionsAndNotInRollout(rolloutId),
|
||||
TargetSpecifications.isCompatibleWithDistributionSetType(distributionSetType.getId()))),
|
||||
pageable)
|
||||
.map(Target.class::cast);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Slice<Target> findByInRolloutGroupWithoutAction(final long group, final Pageable pageable) {
|
||||
if (!rolloutGroupRepository.existsById(group)) {
|
||||
throw new EntityNotFoundException(RolloutGroup.class, group);
|
||||
}
|
||||
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(
|
||||
jpaRepository, List.of(TargetSpecifications.hasNoActionInRolloutGroup(group)), pageable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Target> findByAssignedDistributionSet(final long distributionSetId, final Pageable pageable) {
|
||||
final DistributionSet validDistSet = distributionSetManagement.get(distributionSetId);
|
||||
@@ -289,29 +239,6 @@ public class JpaTargetManagement
|
||||
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
|
||||
final String rsql, final Collection<Long> groups, final DistributionSetType dsType) {
|
||||
return jpaRepository.count(AccessController.Operation.UPDATE,
|
||||
combineWithAnd(List.of(
|
||||
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
|
||||
TargetSpecifications.isNotInRolloutGroups(groups),
|
||||
TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByFailedRolloutAndNotInRolloutGroups(String rolloutId, Collection<Long> groups) {
|
||||
final List<Specification<JpaTarget>> specList = List.of(
|
||||
TargetSpecifications.failedActionsForRollout(rolloutId),
|
||||
TargetSpecifications.isNotInRolloutGroups(groups));
|
||||
return JpaManagementHelper.countBySpec(jpaRepository, specList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long countByActionsInRolloutGroup(final long rolloutGroupId) {
|
||||
return jpaRepository.count(TargetSpecifications.isInActionRolloutGroup(rolloutGroupId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
|
||||
@@ -41,7 +41,7 @@ public class StartNextGroupRolloutGroupSuccessAction implements RolloutGroupActi
|
||||
}
|
||||
|
||||
// Note - the exec could be called by JpaRolloutsExecutor and buy JpaRolloutsManagement#triggerNextGroup
|
||||
// this means it cold be called by concurrently.
|
||||
// this means it could be called by concurrently.
|
||||
@Override
|
||||
public void exec(final Rollout rollout, final RolloutGroup rolloutGroup) {
|
||||
systemSecurityContext.runAsSystem(() -> {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.scheduler;
|
||||
|
||||
import static org.eclipse.hawkbit.repository.jpa.JpaManagementHelper.combineWithAnd;
|
||||
import static org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor.afterCommit;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@@ -25,6 +26,7 @@ import jakarta.persistence.EntityManager;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.ContextAware;
|
||||
import org.eclipse.hawkbit.ql.jpa.QLSupport;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
@@ -33,23 +35,28 @@ import org.eclipse.hawkbit.repository.RolloutExecutor;
|
||||
import org.eclipse.hawkbit.repository.RolloutGroupManagement;
|
||||
import org.eclipse.hawkbit.repository.RolloutHelper;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RolloutStoppedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.management.JpaRolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRollout;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.RolloutTargetGroup;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutTargetGroupRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.rollout.condition.EvaluatorNotConfiguredException;
|
||||
import org.eclipse.hawkbit.repository.jpa.rollout.condition.RolloutGroupEvaluationManager;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
@@ -57,6 +64,7 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.ActionCancellationType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
@@ -64,12 +72,16 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupErrorCondit
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetFields;
|
||||
import org.eclipse.hawkbit.security.SpringSecurityAuditorAware;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
@@ -106,7 +118,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
private final RolloutGroupRepository rolloutGroupRepository;
|
||||
private final RolloutTargetGroupRepository rolloutTargetGroupRepository;
|
||||
private final RolloutRepository rolloutRepository;
|
||||
private final TargetManagement<? extends Target> targetManagement;
|
||||
private final TargetRepository targetRepository;
|
||||
private final DeploymentManagement deploymentManagement;
|
||||
private final RolloutGroupManagement rolloutGroupManagement;
|
||||
private final RolloutManagement rolloutManagement;
|
||||
@@ -117,6 +129,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
private final PlatformTransactionManager txManager;
|
||||
private final TenantAware tenantAware;
|
||||
private final ContextAware contextAware;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final RepositoryProperties repositoryProperties;
|
||||
private final Map<Long, AtomicLong> lastDynamicGroupFill = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -124,17 +137,18 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
public JpaRolloutExecutor(
|
||||
final ActionRepository actionRepository, final RolloutGroupRepository rolloutGroupRepository,
|
||||
final RolloutTargetGroupRepository rolloutTargetGroupRepository,
|
||||
final RolloutRepository rolloutRepository, final TargetManagement<? extends Target> targetManagement,
|
||||
final RolloutRepository rolloutRepository, final TargetRepository targetRepository,
|
||||
final DeploymentManagement deploymentManagement, final RolloutGroupManagement rolloutGroupManagement,
|
||||
final RolloutManagement rolloutManagement, final QuotaManagement quotaManagement,
|
||||
final RolloutGroupEvaluationManager evaluationManager, final RolloutApprovalStrategy rolloutApprovalStrategy,
|
||||
final EntityManager entityManager, final PlatformTransactionManager txManager,
|
||||
final TenantAware tenantAware, final ContextAware contextAware, final RepositoryProperties repositoryProperties) {
|
||||
final TenantAware tenantAware, final ContextAware contextAware, final SystemSecurityContext systemSecurityContext,
|
||||
final RepositoryProperties repositoryProperties) {
|
||||
this.actionRepository = actionRepository;
|
||||
this.rolloutGroupRepository = rolloutGroupRepository;
|
||||
this.rolloutTargetGroupRepository = rolloutTargetGroupRepository;
|
||||
this.rolloutRepository = rolloutRepository;
|
||||
this.targetManagement = targetManagement;
|
||||
this.targetRepository = targetRepository;
|
||||
this.deploymentManagement = deploymentManagement;
|
||||
this.rolloutGroupManagement = rolloutGroupManagement;
|
||||
this.rolloutManagement = rolloutManagement;
|
||||
@@ -145,6 +159,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
this.txManager = txManager;
|
||||
this.tenantAware = tenantAware;
|
||||
this.contextAware = contextAware;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.repositoryProperties = repositoryProperties;
|
||||
}
|
||||
|
||||
@@ -492,7 +507,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
|
||||
private long countTargetsFrom(final JpaRolloutGroup rolloutGroup) {
|
||||
if (rolloutGroup.isDynamic()) {
|
||||
return targetManagement.countByActionsInRolloutGroup(rolloutGroup.getId());
|
||||
return countByActionsInRolloutGroup(rolloutGroup.getId());
|
||||
} else {
|
||||
return rolloutGroupManagement.countTargetsOfRolloutsGroup(rolloutGroup.getId());
|
||||
}
|
||||
@@ -589,13 +604,13 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
targetsInGroupFilter = DeploymentHelper.runInNewTransaction(
|
||||
txManager,
|
||||
"countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable",
|
||||
count -> targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
|
||||
count -> countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
|
||||
groupTargetFilter, readyGroups, rollout.getDistributionSet().getType()));
|
||||
} else { // if it is a rollout retry
|
||||
targetsInGroupFilter = DeploymentHelper.runInNewTransaction(
|
||||
txManager,
|
||||
"countByFailedRolloutAndNotInRolloutGroupsAndCompatible",
|
||||
count -> targetManagement.countByFailedRolloutAndNotInRolloutGroups(
|
||||
count -> countByFailedRolloutAndNotInRolloutGroups(
|
||||
RolloutHelper.getIdFromRetriedTargetFilter(rollout.getTargetFilterQuery()), readyGroups));
|
||||
}
|
||||
|
||||
@@ -640,10 +655,10 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
rollout.getRolloutGroups(), RolloutGroupStatus.READY, group);
|
||||
final Slice<Target> targets;
|
||||
if (!RolloutHelper.isRolloutRetried(rollout.getTargetFilterQuery())) {
|
||||
targets = targetManagement.findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
|
||||
targets = findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
|
||||
readyGroups, targetFilter, rollout.getDistributionSet().getType(), pageRequest);
|
||||
} else {
|
||||
targets = targetManagement.findByFailedRolloutAndNotInRolloutGroups(
|
||||
targets = findByFailedRolloutAndNotInRolloutGroups(
|
||||
RolloutHelper.getIdFromRetriedTargetFilter(rollout.getTargetFilterQuery()), readyGroups, pageRequest);
|
||||
}
|
||||
|
||||
@@ -681,7 +696,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
// since it contains condition for device to be created before the rollout
|
||||
rollout.getTargetFilterQuery(), group);
|
||||
|
||||
final Slice<Target> targets = targetManagement.findByRsqlAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
|
||||
final Slice<Target> targets = findByRsqlAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
|
||||
rollout.getId(), groupTargetFilter, rollout.getDistributionSet().getType(),
|
||||
PageRequest.of(0, Math.toIntExact(Math.min(TRANSACTION_TARGETS, targetsLeftToAdd))));
|
||||
if (targets.getNumberOfElements() > 0) {
|
||||
@@ -798,7 +813,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
|
||||
private Long createActionsForTargetsInNewTransaction(final Rollout rollout, final RolloutGroup group) {
|
||||
return DeploymentHelper.runInNewTransaction(txManager, "createActionsForTargets", status -> {
|
||||
final Slice<Target> targets = targetManagement.findByInRolloutGroupWithoutAction(
|
||||
final Slice<Target> targets = findByInRolloutGroupWithoutAction(
|
||||
group.getId(), PageRequest.of(0, JpaRolloutExecutor.TRANSACTION_TARGETS));
|
||||
|
||||
if (targets.getNumberOfElements() > 0) {
|
||||
@@ -858,7 +873,80 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
try {
|
||||
QuotaHelper.assertAssignmentQuota(target.getId(), requested, quota, Action.class, Target.class, actionRepository::countByTargetId);
|
||||
} catch (final AssignmentQuotaExceededException ex) {
|
||||
deploymentManagement.handleMaxAssignmentsExceeded(target.getId(), requested, ex);
|
||||
systemSecurityContext.runAsSystem(() -> deploymentManagement.handleMaxAssignmentsExceeded(target.getId(), requested, ex));
|
||||
}
|
||||
}
|
||||
|
||||
// target repository access
|
||||
|
||||
// package-private just for testing
|
||||
Slice<Target> findByRsqlAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
|
||||
final long rolloutId, final String rsql, final DistributionSetType distributionSetType, final Pageable pageable) {
|
||||
return targetRepository
|
||||
.findAllWithoutCount(AccessController.Operation.UPDATE,
|
||||
combineWithAnd(List.of(
|
||||
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
|
||||
TargetSpecifications.hasNoOverridingActionsAndNotInRollout(rolloutId),
|
||||
TargetSpecifications.isCompatibleWithDistributionSetType(distributionSetType.getId()))),
|
||||
pageable)
|
||||
.map(Target.class::cast);
|
||||
}
|
||||
|
||||
// Finds all targets for all the given parameter {@link TargetFilterQuery} and that are not assigned to one of the {@link RolloutGroup}s
|
||||
// and are compatible with the passed {@link DistributionSetType}
|
||||
private Slice<Target> findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
|
||||
final Collection<Long> groups, final String rsql, final DistributionSetType dsType, final Pageable pageable) {
|
||||
return targetRepository
|
||||
.findAllWithoutCount(AccessController.Operation.UPDATE,
|
||||
combineWithAnd(List.of(
|
||||
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
|
||||
TargetSpecifications.isNotInRolloutGroups(groups),
|
||||
TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))),
|
||||
pageable)
|
||||
.map(Target.class::cast);
|
||||
}
|
||||
|
||||
// Finds all targets with failed actions for specific Rollout and that are not assigned to one of the retried {@link RolloutGroup}s and
|
||||
// are compatible with the passed {@link DistributionSetType}.
|
||||
private Slice<Target> findByFailedRolloutAndNotInRolloutGroups(
|
||||
final String rolloutId, final Collection<Long> groups, final Pageable pageable) {
|
||||
final List<Specification<JpaTarget>> specList = List.of(
|
||||
TargetSpecifications.failedActionsForRollout(rolloutId),
|
||||
TargetSpecifications.isNotInRolloutGroups(groups));
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, specList, pageable);
|
||||
}
|
||||
|
||||
// Finds all targets of the provided {@link RolloutGroup} that have no Action for the RolloutGroup
|
||||
private Slice<Target> findByInRolloutGroupWithoutAction(final long group, final Pageable pageable) {
|
||||
if (!rolloutGroupRepository.existsById(group)) {
|
||||
throw new EntityNotFoundException(RolloutGroup.class, group);
|
||||
}
|
||||
|
||||
return JpaManagementHelper.findAllWithoutCountBySpec(
|
||||
targetRepository, List.of(TargetSpecifications.hasNoActionInRolloutGroup(group)), pageable);
|
||||
}
|
||||
|
||||
private long countByActionsInRolloutGroup(final long rolloutGroupId) {
|
||||
return targetRepository.count(TargetSpecifications.isInActionRolloutGroup(rolloutGroupId));
|
||||
}
|
||||
|
||||
// Counts all targets for all the given parameter {@link TargetFilterQuery} and that are not assigned to one of the {@link RolloutGroup}s
|
||||
// and are compatible with the passed {@link DistributionSetType}
|
||||
private long countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
|
||||
final String rsql, final Collection<Long> groups, final DistributionSetType dsType) {
|
||||
return targetRepository.count(AccessController.Operation.UPDATE,
|
||||
combineWithAnd(List.of(
|
||||
QLSupport.getInstance().buildSpec(rsql, TargetFields.class),
|
||||
TargetSpecifications.isNotInRolloutGroups(groups),
|
||||
TargetSpecifications.isCompatibleWithDistributionSetType(dsType.getId()))));
|
||||
}
|
||||
|
||||
// Counts all targets with failed actions for specific Rollout and that are not assigned to one of the {@link RolloutGroup}s and are
|
||||
// compatible with the passed {@link DistributionSetType}
|
||||
private long countByFailedRolloutAndNotInRolloutGroups(final String rolloutId, final Collection<Long> groups) {
|
||||
final List<Specification<JpaTarget>> specList = List.of(
|
||||
TargetSpecifications.failedActionsForRollout(rolloutId),
|
||||
TargetSpecifications.isNotInRolloutGroups(groups));
|
||||
return JpaManagementHelper.countBySpec(targetRepository, specList);
|
||||
}
|
||||
}
|
||||
@@ -160,35 +160,18 @@ class TargetManagementTest extends AbstractAccessControllerManagementTest {
|
||||
READ_TARGET, UPDATE_TARGET + "/type.id==" + targetType1.getId(),
|
||||
READ_DISTRIBUTION_SET,
|
||||
CREATE_ROLLOUT, READ_ROLLOUT, HANDLE_ROLLOUT), () -> {
|
||||
assertThat(targetManagement.findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(
|
||||
List.of(1L), "id==*", ds2Type2.getType(), UNPAGED).stream().toList())
|
||||
.containsExactly(target1Type1);
|
||||
assertThat(targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable("id==*", List.of(1L), ds2Type2.getType()))
|
||||
.isEqualTo(1);
|
||||
|
||||
final Rollout rollout = testdataFactory.createRolloutByVariables("testRollout", "testDescription", 3, "id==*", ds2Type2, "50", "5");
|
||||
final List<Long> groups = rolloutGroupManagement.findByRollout(rollout.getId(), UNPAGED).getContent().stream().
|
||||
map(Identifiable::getId).toList();
|
||||
assertThat(groups.stream().flatMap(
|
||||
group -> targetManagement.findByInRolloutGroupWithoutAction(group, UNPAGED).get()).toList())
|
||||
.containsExactly(target1Type1);
|
||||
assertThat(groups.stream().flatMap(
|
||||
group -> rolloutGroupManagement.findTargetsOfRolloutGroup(group, UNPAGED).get()).toList())
|
||||
.containsExactly(target1Type1);
|
||||
|
||||
assertThat(targetManagement.findByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable(groups, "id==*", ds2Type2.getType(), UNPAGED))
|
||||
.isEmpty();
|
||||
assertThat(targetManagement.countByRsqlAndNotInRolloutGroupsAndCompatibleAndUpdatable("id==*", groups, ds2Type2.getType()))
|
||||
.isZero();
|
||||
|
||||
// as system in context - doesn't apply scopes
|
||||
final Rollout runAsSystem = systemSecurityContext.runAsSystem(
|
||||
() -> testdataFactory.createRolloutByVariables(
|
||||
"testRolloutAsSystem", "testDescriptionAsSystem", 3, "id==*", ds2Type2, "50", "5"));
|
||||
assertThat(rolloutGroupManagement.findByRollout(runAsSystem.getId(), UNPAGED).getContent().stream()
|
||||
.flatMap(
|
||||
group -> targetManagement.findByInRolloutGroupWithoutAction(group.getId(), UNPAGED).getContent().stream()).toList())
|
||||
.hasSize(3);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -672,8 +672,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
final List<DeploymentRequest> deploymentRequests = createAssignmentRequests(distributionSets, targets, 34);
|
||||
|
||||
enableMultiAssignments();
|
||||
final List<DistributionSetAssignmentResult> results = deploymentManagement
|
||||
.assignDistributionSets(deploymentRequests);
|
||||
final List<DistributionSetAssignmentResult> results = deploymentManagement.assignDistributionSets(deploymentRequests);
|
||||
|
||||
assertThat(getResultingActionCount(results)).isEqualTo(deploymentRequests.size());
|
||||
final List<Long> dsIds = distributionSets.stream().map(DistributionSet::getId).toList();
|
||||
|
||||
@@ -38,6 +38,7 @@ import io.github.classgraph.ClassInfo;
|
||||
import io.github.classgraph.ScanResult;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.PermissionSupport;
|
||||
import org.eclipse.hawkbit.repository.TenantStatsManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||
@@ -257,7 +258,8 @@ class ManagementSecurityTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(spelNode.getChild(0) instanceof VariableReference varRef && varRef.toStringAST().equals("#root")).isTrue();
|
||||
assertThat(spelNode.getChild(1)).isInstanceOf(StringLiteral.class);
|
||||
final StringLiteral literal = (StringLiteral) spelNode.getChild(1);
|
||||
preAuthorizedPermissions.add(literal.getLiteralValue().getValue() + "_" + permissionGroup);
|
||||
preAuthorizedPermissions.add(String.valueOf(literal.getLiteralValue().getValue())
|
||||
.replace(SpringEvalExpressions.PERMISSION_GROUP_PLACEHOLDER, permissionGroup));
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected MethodReference: " + method);
|
||||
}
|
||||
|
||||
@@ -10,10 +10,13 @@
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.callAs;
|
||||
import static org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch.withUser;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
@@ -24,8 +27,11 @@ import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
import org.eclipse.hawkbit.security.SecurityContextSerializer;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
@@ -39,6 +45,15 @@ import org.springframework.test.context.TestPropertySource;
|
||||
@TestPropertySource(properties = { "hawkbit.server.repository.dynamicRolloutsMinInvolvePeriodMS=-1" })
|
||||
class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
SecurityContextSerializer securityContextSerializer() {
|
||||
return SecurityContextSerializer.JSON_SERIALIZATION;
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void reset() {
|
||||
this.approvalStrategy.setApprovalNeeded(false);
|
||||
@@ -47,19 +62,21 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
||||
/**
|
||||
* Verifies a simple rollout flow
|
||||
*/
|
||||
@SneakyThrows
|
||||
@Test
|
||||
void rolloutFlow() {
|
||||
final String rolloutName = "rollout-std";
|
||||
final int amountGroups = 5; // static only
|
||||
final String targetPrefix = "controller-rollout-std-";
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsFor" + rolloutName);
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSetLocked("dsFor" + rolloutName);
|
||||
|
||||
testdataFactory.createTargets(targetPrefix, 0, amountGroups * 3);
|
||||
final Rollout rollout = testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30", false, false);
|
||||
final Rollout rollout = callAs(
|
||||
withUser("rolloutFlowUser", "READ_DISTRIBUTION_SET", "READ_TARGET", "READ_ROLLOUT", "CREATE_ROLLOUT"),
|
||||
() -> testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30", false, false));
|
||||
final List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(
|
||||
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))
|
||||
).getContent();
|
||||
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))).getContent();
|
||||
|
||||
// add 2 targets not to be included
|
||||
testdataFactory.createTargets(targetPrefix, amountGroups * 3, 2);
|
||||
@@ -89,22 +106,24 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
||||
/**
|
||||
* Verifies a simple dynamic rollout flow
|
||||
*/
|
||||
@SneakyThrows
|
||||
@Test
|
||||
void dynamicRolloutFlow() {
|
||||
final String rolloutName = "dynamic-rollout-std";
|
||||
final int amountGroups = 2; // static only
|
||||
final String targetPrefix = "controller-dynamic-rollout-std-";
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsFor" + rolloutName);
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSetLocked("dsFor" + rolloutName);
|
||||
|
||||
testdataFactory.createTargets(targetPrefix, 0, amountGroups * 3);
|
||||
final Rollout rollout = testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30", false, true);
|
||||
final Rollout rollout = callAs(
|
||||
withUser("dynamicRolloutFlow", "READ_DISTRIBUTION_SET", "READ_TARGET", "READ_ROLLOUT", "CREATE_ROLLOUT"),
|
||||
() -> testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30", false, true));
|
||||
|
||||
// rollout is READY
|
||||
assertRollout(rollout, true, RolloutStatus.READY, amountGroups + 1, amountGroups * 3);
|
||||
List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(
|
||||
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))
|
||||
).getContent();
|
||||
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))).getContent();
|
||||
final RolloutGroup dynamic1 = groups.get(amountGroups);
|
||||
assertRollout(rollout, true, RolloutStatus.READY, amountGroups + 1, amountGroups * 3); // + dynamic
|
||||
for (int i = 0; i < amountGroups; i++) {
|
||||
@@ -139,8 +158,7 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
||||
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 2, amountGroups * 3 + 3);
|
||||
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 3);
|
||||
groups = rolloutGroupManagement.findByRollout(
|
||||
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))
|
||||
).getContent();
|
||||
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))).getContent();
|
||||
final RolloutGroup dynamic2 = groups.get(amountGroups + 1);
|
||||
assertGroup(dynamic2, true, RolloutGroupStatus.SCHEDULED, 0);
|
||||
|
||||
@@ -205,25 +223,27 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
||||
/**
|
||||
* Verifies a simple dynamic rollout flow with a dynamic group template
|
||||
*/
|
||||
@SneakyThrows
|
||||
@Test
|
||||
void dynamicRolloutTemplateFlow() {
|
||||
final String rolloutName = "dynamic-template-rollout-std";
|
||||
final int amountGroups = 3; // static only
|
||||
final String targetPrefix = "controller-template-dynamic-rollout-std-";
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsFor" + rolloutName);
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSetLocked("dsFor" + rolloutName);
|
||||
|
||||
// create rollout with amountGroups static groups * 3 targets and dynamic group template with 6 targets
|
||||
testdataFactory.createTargets(targetPrefix, 0, amountGroups * 3);
|
||||
final Rollout rollout = testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30",
|
||||
Action.ActionType.FORCED, 1000, false, true,
|
||||
RolloutManagement.DynamicRolloutGroupTemplate.builder().nameSuffix("-dyn").targetCount(6).build());
|
||||
final Rollout rollout = callAs(
|
||||
withUser("dynamicRolloutTemplateFlow", "READ_DISTRIBUTION_SET", "READ_TARGET", "READ_ROLLOUT", "CREATE_ROLLOUT"),
|
||||
() -> testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30",
|
||||
Action.ActionType.FORCED, 1000, false, true,
|
||||
RolloutManagement.DynamicRolloutGroupTemplate.builder().nameSuffix("-dyn").targetCount(6).build()));
|
||||
|
||||
// rollout is READY, amountGroups + 1 (dynamic) rollout groups and amountGroups * 3 targets in static groups
|
||||
assertRollout(rollout, true, RolloutStatus.READY, amountGroups + 1, amountGroups * 3);
|
||||
List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(
|
||||
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))
|
||||
).getContent();
|
||||
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))).getContent();
|
||||
final RolloutGroup dynamic1 = groups.get(amountGroups);
|
||||
assertRollout(rollout, true, RolloutStatus.READY, amountGroups + 1, amountGroups * 3); // + dynamic
|
||||
for (int i = 0; i < amountGroups; i++) {
|
||||
@@ -258,8 +278,7 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
||||
assertRollout(rollout, true, RolloutStatus.RUNNING, amountGroups + 2, amountGroups * 3 + 6);
|
||||
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 6);
|
||||
groups = rolloutGroupManagement.findByRollout(
|
||||
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))
|
||||
).getContent();
|
||||
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))).getContent();
|
||||
final RolloutGroup dynamic2 = groups.get(amountGroups + 1);
|
||||
assertGroup(dynamic2, true, RolloutGroupStatus.SCHEDULED, 0);
|
||||
|
||||
@@ -305,16 +324,19 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
||||
/**
|
||||
* Verifies a simple pure (no static groups) dynamic rollout flow with a dynamic group template
|
||||
*/
|
||||
@SneakyThrows
|
||||
@Test
|
||||
void dynamicRolloutPureFlow() {
|
||||
final String rolloutName = "pure-dynamic-rollout-std";
|
||||
final String targetPrefix = "controller-pure-dynamic-rollout-std-";
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsFor" + rolloutName);
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSetLocked("dsFor" + rolloutName);
|
||||
|
||||
final Rollout rollout = testdataFactory.createRolloutByVariables(rolloutName, rolloutName, 0,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30",
|
||||
Action.ActionType.FORCED, 1000, false, true,
|
||||
RolloutManagement.DynamicRolloutGroupTemplate.builder().nameSuffix("-dyn").targetCount(6).build());
|
||||
final Rollout rollout = callAs(
|
||||
withUser("dynamicRolloutPureFlow", "READ_DISTRIBUTION_SET", "READ_TARGET", "READ_ROLLOUT", "CREATE_ROLLOUT"),
|
||||
() -> testdataFactory.createRolloutByVariables(rolloutName, rolloutName, 0,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "60", "30",
|
||||
Action.ActionType.FORCED, 1000, false, true,
|
||||
RolloutManagement.DynamicRolloutGroupTemplate.builder().nameSuffix("-dyn").targetCount(6).build()));
|
||||
|
||||
// rollout is READY, amountGroups + 1 (dynamic) rollout groups and amountGroups * 3 targets in static groups
|
||||
assertRollout(rollout, true, RolloutStatus.READY, 1, 0);
|
||||
@@ -347,8 +369,7 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
||||
assertRollout(rollout, true, RolloutStatus.RUNNING, 2, 6);
|
||||
assertGroup(dynamic1, true, RolloutGroupStatus.RUNNING, 6);
|
||||
groups = rolloutGroupManagement.findByRollout(
|
||||
rollout.getId(), new OffsetBasedPageRequest(0, 10, Sort.by(Direction.ASC, "id"))
|
||||
).getContent();
|
||||
rollout.getId(), new OffsetBasedPageRequest(0, 10, Sort.by(Direction.ASC, "id"))).getContent();
|
||||
final RolloutGroup dynamic2 = groups.get(1);
|
||||
assertGroup(dynamic2, true, RolloutGroupStatus.SCHEDULED, 0);
|
||||
|
||||
@@ -388,19 +409,21 @@ class RolloutManagementFlowTest extends AbstractJpaIntegrationTest {
|
||||
/**
|
||||
* Verifies a simple rollout flow
|
||||
*/
|
||||
@SneakyThrows
|
||||
@Test
|
||||
void rollout0ThresholdFlow() {
|
||||
final String rolloutName = "rollout-std-0threshold";
|
||||
final int amountGroups = 5; // static only
|
||||
final String targetPrefix = "controller-rollout-std-0threshold-";
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsFor" + rolloutName);
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSetLocked("dsFor" + rolloutName);
|
||||
|
||||
testdataFactory.createTargets(targetPrefix, 0, amountGroups * 3);
|
||||
final Rollout rollout = testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "0", "25", false, false);
|
||||
final Rollout rollout = callAs(
|
||||
withUser("rollout0ThresholdFlow", "READ_DISTRIBUTION_SET", "READ_TARGET", "READ_ROLLOUT", "CREATE_ROLLOUT"),
|
||||
() -> testdataFactory.createRolloutByVariables(rolloutName, rolloutName, amountGroups,
|
||||
"controllerid==" + targetPrefix + "*", distributionSet, "0", "25", false, false));
|
||||
final List<RolloutGroup> groups = rolloutGroupManagement.findByRollout(
|
||||
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))
|
||||
).getContent();
|
||||
rollout.getId(), new OffsetBasedPageRequest(0, amountGroups + 10, Sort.by(Direction.ASC, "id"))).getContent();
|
||||
|
||||
// start rollout
|
||||
rolloutManagement.start(rollout.getId());
|
||||
|
||||
@@ -57,17 +57,14 @@ import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaNamedEntity_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionStatusCreate;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Tag;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
@@ -79,7 +76,6 @@ import org.eclipse.hawkbit.repository.test.util.SecurityContextSwitch;
|
||||
import org.eclipse.hawkbit.repository.test.util.WithUser;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
/**
|
||||
@@ -729,60 +725,6 @@ class TargetManagementTest extends AbstractRepositoryManagementWithMetadataTest<
|
||||
"notExisting", ds.getId(), "name==*")).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests action based aspects of the dynamic group assignment filters.
|
||||
* Target matches filter no active action with ge weight.
|
||||
*/
|
||||
@Test
|
||||
void findByTargetFilterQueryAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable() {
|
||||
final String targetPrefix = "dyn_action_filter_";
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
final List<Target> targets = testdataFactory.createTargets(targetPrefix, 10);
|
||||
final Rollout rolloutOlder = testdataFactory.createRollout();
|
||||
final Rollout rollout = testdataFactory.createRollout();
|
||||
final Rollout rolloutNewer = testdataFactory.createRollout();
|
||||
|
||||
int target = 0;
|
||||
final List<Integer> expected = new ArrayList<>();
|
||||
// old ro with less weight - match
|
||||
expected.add(target);
|
||||
createAction(targets.get(target++), rolloutOlder, 0, Status.RUNNING, distributionSet);
|
||||
// old ro with equal weight - match
|
||||
expected.add(target);
|
||||
createAction(targets.get(target++), rolloutOlder, 10, Status.RUNNING, distributionSet);
|
||||
// old ro with bigger weight, scheduled - match
|
||||
expected.add(target);
|
||||
createAction(targets.get(target++), rolloutOlder, 11, Status.SCHEDULED, distributionSet);
|
||||
// old ro with bigger weight, running - match
|
||||
expected.add(target);
|
||||
createAction(targets.get(target++), rolloutOlder, 11, Status.RUNNING, distributionSet);
|
||||
// old ro with bigger weight, running - match
|
||||
expected.add(target);
|
||||
createAction(targets.get(target++), rolloutOlder, 11, Status.FINISHED, distributionSet);
|
||||
// same ro - doesn't match
|
||||
createAction(targets.get(target++), rollout, 10, Status.RUNNING, distributionSet);
|
||||
// new ro with less weight - doesn't match
|
||||
createAction(targets.get(target++), rolloutNewer, 0, Status.RUNNING, distributionSet);
|
||||
// new ro with less weight - doesn't match
|
||||
createAction(targets.get(target++), rolloutNewer, 5, Status.WARNING, distributionSet);
|
||||
// NEW ro with EQUAL weight - doesn't match
|
||||
createAction(targets.get(target++), rolloutNewer, 10, Status.RUNNING, distributionSet);
|
||||
// new ro with BIGGER weight - doesn't match
|
||||
createAction(targets.get(target), rolloutNewer, 20, Status.DOWNLOADED, distributionSet);
|
||||
|
||||
final Slice<Target> matching =
|
||||
targetManagement.findByRsqlAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
|
||||
rollout.getId(), "controllerid==dyn_action_filter_*", distributionSet.getType(), PAGE);
|
||||
|
||||
assertThat(matching.getNumberOfElements()).isEqualTo(expected.size());
|
||||
assertThat(matching.stream()
|
||||
.map(Target::getControllerId)
|
||||
.map(s -> s.substring(targetPrefix.length()))
|
||||
.map(Integer::parseInt)
|
||||
.sorted()
|
||||
.toList()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Target matches filter for not existing DS.
|
||||
*/
|
||||
@@ -834,7 +776,6 @@ class TargetManagementTest extends AbstractRepositoryManagementWithMetadataTest<
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.findByTargetFilterQueryAndNonDSAndCompatibleAndUpdatable(NOT_EXIST_IDL, "name==*", PAGE),
|
||||
"DistributionSet");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByInRolloutGroupWithoutAction(NOT_EXIST_IDL, PAGE), "RolloutGroup");
|
||||
verifyThrownExceptionBy(() -> targetManagement.findByAssignedDistributionSet(NOT_EXIST_IDL, PAGE), "DistributionSet");
|
||||
verifyThrownExceptionBy(
|
||||
() -> targetManagement.findByAssignedDistributionSetAndRsql(NOT_EXIST_IDL, "name==*", PAGE), "DistributionSet");
|
||||
@@ -1124,24 +1065,6 @@ class TargetManagementTest extends AbstractRepositoryManagementWithMetadataTest<
|
||||
}
|
||||
}
|
||||
|
||||
private void createAction(final Target target, final Rollout rollout, final Integer weight, final Action.Status status,
|
||||
final DistributionSet distributionSet) {
|
||||
final JpaAction action = new JpaAction();
|
||||
action.setActionType(Action.ActionType.FORCED);
|
||||
action.setTarget(target);
|
||||
action.setInitiatedBy("test");
|
||||
if (rollout != null) {
|
||||
action.setRollout(rollout);
|
||||
}
|
||||
if (weight != null) {
|
||||
action.setWeight(weight);
|
||||
}
|
||||
action.setStatus(status);
|
||||
action.setActive(status != Status.FINISHED && status != Status.ERROR && status != Status.CANCELED);
|
||||
action.setDistributionSet(distributionSet);
|
||||
actionRepository.save(action);
|
||||
}
|
||||
|
||||
private void validateFoundTargetsByRsql(final String rsqlFilter, final String... controllerIds) {
|
||||
final Page<? extends Target> foundTargetsByMetadataAndControllerId = targetManagement.findByRsql(rsqlFilter, PAGE);
|
||||
final long foundTargetsByMetadataAndControllerIdCount = targetManagement.countByRsql(rsqlFilter);
|
||||
|
||||
@@ -50,7 +50,7 @@ import org.springframework.data.domain.Slice;
|
||||
* Story: Auto assign checker
|
||||
*/
|
||||
@SuppressWarnings("java:S6813") // constructor injects are not possible for test classes
|
||||
class JpaAutoAssignExecutorIntTest extends AbstractJpaIntegrationTest {
|
||||
class AutoAssignExecutorIntTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
private static final String SPACE_AND_DESCRIPTION = " description";
|
||||
|
||||
@@ -45,7 +45,7 @@ import org.springframework.transaction.PlatformTransactionManager;
|
||||
* Story: Auto assign checker
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class JpaAutoAssignExecutorTest {
|
||||
class AutoAssignExecutorTest {
|
||||
|
||||
@Mock
|
||||
private TargetFilterQueryManagement<? extends TargetFilterQuery> targetFilterQueryManagement;
|
||||
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Contributors to the Eclipse Foundation
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.scheduler;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Slice;
|
||||
|
||||
public class RolloutExecutorTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private JpaRolloutExecutor jpaRolloutExecutor;
|
||||
|
||||
/**
|
||||
* Tests action based aspects of the dynamic group assignment filters.
|
||||
* Target matches filter no active action with ge weight.
|
||||
*/
|
||||
@Test
|
||||
void findByTargetFilterQueryAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable() {
|
||||
final String targetPrefix = "dyn_action_filter_";
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
final List<Target> targets = testdataFactory.createTargets(targetPrefix, 10);
|
||||
final Rollout rolloutOlder = testdataFactory.createRollout();
|
||||
final Rollout rollout = testdataFactory.createRollout();
|
||||
final Rollout rolloutNewer = testdataFactory.createRollout();
|
||||
|
||||
int target = 0;
|
||||
final List<Integer> expected = new ArrayList<>();
|
||||
// old ro with less weight - match
|
||||
expected.add(target);
|
||||
createAction(targets.get(target++), rolloutOlder, 0, Action.Status.RUNNING, distributionSet);
|
||||
// old ro with equal weight - match
|
||||
expected.add(target);
|
||||
createAction(targets.get(target++), rolloutOlder, 10, Action.Status.RUNNING, distributionSet);
|
||||
// old ro with bigger weight, scheduled - match
|
||||
expected.add(target);
|
||||
createAction(targets.get(target++), rolloutOlder, 11, Action.Status.SCHEDULED, distributionSet);
|
||||
// old ro with bigger weight, running - match
|
||||
expected.add(target);
|
||||
createAction(targets.get(target++), rolloutOlder, 11, Action.Status.RUNNING, distributionSet);
|
||||
// old ro with bigger weight, running - match
|
||||
expected.add(target);
|
||||
createAction(targets.get(target++), rolloutOlder, 11, Action.Status.FINISHED, distributionSet);
|
||||
// same ro - doesn't match
|
||||
createAction(targets.get(target++), rollout, 10, Action.Status.RUNNING, distributionSet);
|
||||
// new ro with less weight - doesn't match
|
||||
createAction(targets.get(target++), rolloutNewer, 0, Action.Status.RUNNING, distributionSet);
|
||||
// new ro with less weight - doesn't match
|
||||
createAction(targets.get(target++), rolloutNewer, 5, Action.Status.WARNING, distributionSet);
|
||||
// NEW ro with EQUAL weight - doesn't match
|
||||
createAction(targets.get(target++), rolloutNewer, 10, Action.Status.RUNNING, distributionSet);
|
||||
// new ro with BIGGER weight - doesn't match
|
||||
createAction(targets.get(target), rolloutNewer, 20, Action.Status.DOWNLOADED, distributionSet);
|
||||
|
||||
final Slice<Target> matching =
|
||||
jpaRolloutExecutor.findByRsqlAndNoOverridingActionsAndNotInRolloutAndCompatibleAndUpdatable(
|
||||
rollout.getId(), "controllerid==dyn_action_filter_*", distributionSet.getType(), PAGE);
|
||||
|
||||
assertThat(matching.getNumberOfElements()).isEqualTo(expected.size());
|
||||
assertThat(matching.stream()
|
||||
.map(Target::getControllerId)
|
||||
.map(s -> s.substring(targetPrefix.length()))
|
||||
.map(Integer::parseInt)
|
||||
.sorted()
|
||||
.toList()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
private void createAction(
|
||||
final Target target, final Rollout rollout, final Integer weight, final Action.Status status, final DistributionSet distributionSet) {
|
||||
final JpaAction action = new JpaAction();
|
||||
action.setActionType(Action.ActionType.FORCED);
|
||||
action.setTarget(target);
|
||||
action.setInitiatedBy("test");
|
||||
if (rollout != null) {
|
||||
action.setRollout(rollout);
|
||||
}
|
||||
if (weight != null) {
|
||||
action.setWeight(weight);
|
||||
}
|
||||
action.setStatus(status);
|
||||
action.setActive(status != Action.Status.FINISHED && status != Action.Status.ERROR && status != Action.Status.CANCELED);
|
||||
action.setDistributionSet(distributionSet);
|
||||
actionRepository.save(action);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user