Multi-Asssignments feature removal (#2893)

* Multi-Asssignments feature removal

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

* fix some sonar findings

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

* fixes after review

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>

---------

Signed-off-by: strailov <Stanislav.Trailov@bosch.io>
This commit is contained in:
Stanislav Trailov
2026-02-04 16:59:09 +02:00
committed by GitHub
parent 2bf443661d
commit c33156b134
43 changed files with 123 additions and 1610 deletions

View File

@@ -65,7 +65,6 @@ public abstract class AbstractDsAssignmentStrategy {
private final ActionStatusRepository actionStatusRepository;
private final QuotaManagement quotaManagement;
private final BooleanSupplier multiAssignmentsConfig;
private final BooleanSupplier confirmationFlowConfig;
private final RepositoryProperties repositoryProperties;
private final Consumer<MaxAssignmentsExceededInfo> maxAssignmentExceededHandler;
@@ -74,14 +73,13 @@ public abstract class AbstractDsAssignmentStrategy {
AbstractDsAssignmentStrategy(
final TargetRepository targetRepository,
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties,
final QuotaManagement quotaManagement, final BooleanSupplier confirmationFlowConfig,
final RepositoryProperties repositoryProperties,
final Consumer<MaxAssignmentsExceededInfo> maxAssignmentExceededHandler) {
this.targetRepository = targetRepository;
this.actionRepository = actionRepository;
this.actionStatusRepository = actionStatusRepository;
this.quotaManagement = quotaManagement;
this.multiAssignmentsConfig = multiAssignmentsConfig;
this.confirmationFlowConfig = confirmationFlowConfig;
this.repositoryProperties = repositoryProperties;
this.maxAssignmentExceededHandler = maxAssignmentExceededHandler;
@@ -211,10 +209,6 @@ public abstract class AbstractDsAssignmentStrategy {
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new CancelTargetAssignmentEvent(action)));
}
protected boolean isMultiAssignmentsEnabled() {
return multiAssignmentsConfig.getAsBoolean();
}
protected boolean isConfirmationFlowEnabled() {
return confirmationFlowConfig.getAsBoolean();
}

View File

@@ -52,7 +52,7 @@ import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedExcepti
import org.eclipse.hawkbit.repository.exception.IncompatibleTargetTypeException;
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
import org.eclipse.hawkbit.repository.exception.MultiAssignmentException;
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
import org.eclipse.hawkbit.repository.jpa.Jpa;
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
@@ -159,10 +159,10 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
maxAssignmentsExceededInfo.requested,
maxAssignmentsExceededInfo.quotaExceededException);
onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, actionRepository, actionStatusRepository,
quotaManagement, this::isMultiAssignmentsEnabled, this::isConfirmationFlowEnabled, repositoryProperties,
quotaManagement, this::isConfirmationFlowEnabled, repositoryProperties,
maxAssignmentsExceededHandler);
offlineDsAssignmentStrategy = new OfflineDsAssignmentStrategy(targetRepository, actionRepository, actionStatusRepository,
quotaManagement, this::isMultiAssignmentsEnabled, this::isConfirmationFlowEnabled, repositoryProperties,
quotaManagement, this::isConfirmationFlowEnabled, repositoryProperties,
maxAssignmentsExceededHandler);
}
@@ -423,16 +423,12 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
public void cancelInactiveScheduledActionsForTargets(final List<Long> targetIds) {
if (!isMultiAssignmentsEnabled()) {
targetRepository.getAccessController().ifPresent(v -> {
if (targetRepository.count(AccessController.Operation.UPDATE, TargetSpecifications.hasIdIn(targetIds)) != targetIds.size()) {
throw new EntityNotFoundException(Target.class, targetIds);
}
});
actionRepository.switchStatus(Status.CANCELED, targetIds, false, Status.SCHEDULED);
} else {
log.debug("The Multi Assignments feature is enabled: No need to cancel inactive scheduled actions.");
}
targetRepository.getAccessController().ifPresent(v -> {
if (targetRepository.count(AccessController.Operation.UPDATE, TargetSpecifications.hasIdIn(targetIds)) != targetIds.size()) {
throw new EntityNotFoundException(Target.class, targetIds);
}
});
actionRepository.switchStatus(Status.CANCELED, targetIds, false, Status.SCHEDULED);
}
@Override
@@ -668,12 +664,10 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
}
private void checkForMultiAssignment(final Collection<DeploymentRequest> deploymentRequests) {
if (!isMultiAssignmentsEnabled()) {
final long distinctTargetsInRequest = deploymentRequests.stream()
.map(request -> request.getTargetWithActionType().getControllerId()).distinct().count();
if (distinctTargetsInRequest < deploymentRequests.size()) {
throw new MultiAssignmentIsNotEnabledException();
}
final long distinctTargetsInRequest = deploymentRequests.stream()
.map(request -> request.getTargetWithActionType().getControllerId()).distinct().count();
if (distinctTargetsInRequest < deploymentRequests.size()) {
throw new MultiAssignmentException();
}
}
@@ -819,9 +813,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
final List<JpaTarget> targetEntities) {
final List<List<Long>> targetEntitiesIdsChunks = getTargetEntitiesAsChunks(targetEntities);
if (!isMultiAssignmentsEnabled()) {
closeOrCancelActiveActions(assignmentStrategy, targetEntitiesIdsChunks);
}
closeOrCancelActiveActions(assignmentStrategy, targetEntitiesIdsChunks);
// cancel all scheduled actions which are in-active, these actions were
// not active before and the manual assignment which has been done cancels them
targetEntitiesIdsChunks.forEach(this::cancelInactiveScheduledActionsForTargets);
@@ -944,10 +936,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
}
private JpaAction closeActionIfSetWasAlreadyAssigned(final JpaAction action) {
if (isMultiAssignmentsEnabled()) {
return action;
}
final JpaTarget target = action.getTarget();
if (target.getAssignedDistributionSet() != null && action.getDistributionSet().getId()
.equals(target.getAssignedDistributionSet().getId())) {
@@ -965,9 +953,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
}
private List<Action> startScheduledActionsAndHandleOpenCancellationFirst(final List<JpaAction> actions) {
if (!isMultiAssignmentsEnabled()) {
closeOrCancelOpenDeviceActions(actions);
}
closeOrCancelOpenDeviceActions(actions);
final List<JpaAction> savedActions = activateActionsOfRolloutGroup(actions);
setInitialActionStatusOfRolloutGroup(savedActions);
setAssignmentOnTargets(savedActions);
@@ -1017,10 +1003,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
actionStatusRepository.save(actionStatus);
}
private boolean isMultiAssignmentsEnabled() {
return TenantConfigHelper.isMultiAssignmentsEnabled();
}
private boolean isConfirmationFlowEnabled() {
return TenantConfigHelper.isUserConfirmationFlowEnabled();
}

View File

@@ -167,7 +167,7 @@ public class JpaRolloutManagement implements RolloutManagement {
this.repositoryProperties = repositoryProperties;
onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, actionRepository, actionStatusRepository,
quotaManagement, this::isMultiAssignmentsEnabled, this::isConfirmationFlowEnabled, repositoryProperties, null);
quotaManagement, this::isConfirmationFlowEnabled, repositoryProperties, null);
}
@Autowired
@@ -554,7 +554,7 @@ public class JpaRolloutManagement implements RolloutManagement {
storeActionsAndStatuses(actions, Action.Status.CANCELING);
// send cancellation messages to event publisher
onlineDsAssignmentStrategy.sendCancellationMessages(actions, AccessContext.tenant());
onlineDsAssignmentStrategy.sendCancellationMessages(actions);
}
private void forceQuitActionsOfRollout(final Rollout rollout) {
@@ -1033,10 +1033,6 @@ public class JpaRolloutManagement implements RolloutManagement {
return new TargetCount(totalTargets, baseFilter);
}
private boolean isMultiAssignmentsEnabled() {
return TenantConfigHelper.isMultiAssignmentsEnabled();
}
private boolean isConfirmationFlowEnabled() {
return TenantConfigHelper.isUserConfirmationFlowEnabled();
}

View File

@@ -11,8 +11,6 @@ package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.auth.SpPermission.READ_GATEWAY_SECURITY_TOKEN;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.POLLING_TIME;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED;
@@ -207,7 +205,6 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
tenantConfiguration.setValue(valueToString);
}
assertValueChangeIsAllowed(e.getKey(), tenantConfiguration);
return tenantConfiguration;
}).toList());
}
@@ -273,58 +270,6 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
return CONVERSION_SERVICE.convert(key.getDefaultValue(), propertyType);
}
/**
* Asserts that the requested configuration value change is allowed. Throws a {@link TenantConfigurationValueChangeNotAllowedException}
* otherwise.
*
* @param key The configuration key.
* @param valueChange The configuration to be validated.
* @throws TenantConfigurationValueChangeNotAllowedException if the requested configuration change is not allowed.
*/
private void assertValueChangeIsAllowed(final String key, final JpaTenantConfiguration valueChange) {
assertMultiAssignmentsValueChange(key, valueChange);
assertAutoCloseValueChange(key);
assertBatchAssignmentValueChange(key, valueChange);
}
private void assertMultiAssignmentsValueChange(final String key, final JpaTenantConfiguration valueChange) {
if (MULTI_ASSIGNMENTS_ENABLED.equals(key) && !Boolean.parseBoolean(valueChange.getValue())) {
log.debug("The Multi-Assignments '{}' feature cannot be disabled.", key);
throw new TenantConfigurationValueChangeNotAllowedException();
}
if (MULTI_ASSIGNMENTS_ENABLED.equals(key) && Boolean.parseBoolean(valueChange.getValue())) {
JpaTenantConfiguration batchConfig = tenantConfigurationRepository.findByKey(BATCH_ASSIGNMENTS_ENABLED);
if (batchConfig != null && Boolean.parseBoolean(batchConfig.getValue())) {
log.debug(
"The Multi-Assignments '{}' feature cannot be enabled as it contradicts with the Batch-Assignments feature, which is already enabled .",
key);
throw new TenantConfigurationValueChangeNotAllowedException();
}
}
}
private void assertAutoCloseValueChange(final String key) {
if (REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED.equals(key)
&& Boolean.TRUE.equals(Optional.ofNullable(getConfigurationValue0(MULTI_ASSIGNMENTS_ENABLED, Boolean.class))
.map(TenantConfigurationValue::getValue)
.orElse(null))) {
log.debug("The property '{}' must not be changed because the Multi-Assignments feature is currently enabled.", key);
throw new TenantConfigurationValueChangeNotAllowedException();
}
}
private void assertBatchAssignmentValueChange(final String key, final JpaTenantConfiguration valueChange) {
if (BATCH_ASSIGNMENTS_ENABLED.equals(key) && Boolean.parseBoolean(valueChange.getValue())) {
final JpaTenantConfiguration multiConfig = tenantConfigurationRepository.findByKey(MULTI_ASSIGNMENTS_ENABLED);
if (multiConfig != null && Boolean.parseBoolean(multiConfig.getValue())) {
log.debug(
"The Batch-Assignments '{}' feature cannot be enabled as it contradicts with the Multi-Assignments feature, which is already enabled .",
key);
throw new TenantConfigurationValueChangeNotAllowedException();
}
}
}
private static PollStatus pollStatus(final long lastTargetQuery, final PollingInterval pollingInterval, final Duration pollingOverdueTime) {
final LocalDateTime currentDate = LocalDateTime.now();
final LocalDateTime lastPollDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastTargetQuery), ZoneId.systemDefault());

View File

@@ -46,11 +46,11 @@ class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
OfflineDsAssignmentStrategy(
final TargetRepository targetRepository,
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
final QuotaManagement quotaManagement,
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties,
final Consumer<MaxAssignmentsExceededInfo> maxAssignmentExceededHandler) {
super(targetRepository, actionRepository, actionStatusRepository,
quotaManagement, multiAssignmentsConfig, confirmationFlowConfig, repositoryProperties, maxAssignmentExceededHandler);
quotaManagement, confirmationFlowConfig, repositoryProperties, maxAssignmentExceededHandler);
}
@Override
@@ -74,14 +74,11 @@ class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
@Override
public List<JpaTarget> findTargetsForAssignment(final List<String> controllerIDs, final long setId) {
final Function<List<String>, List<JpaTarget>> mapper;
if (isMultiAssignmentsEnabled()) {
mapper = ids -> targetRepository.findAll(TargetSpecifications.hasControllerIdIn(ids));
} else {
mapper = ids -> targetRepository.findAll(JpaManagementHelper.combineWithAnd(List.of(
TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, setId),
TargetSpecifications.notEqualToTargetUpdateStatus(TargetUpdateStatus.PENDING))));
}
final Function<List<String>, List<JpaTarget>> mapper =
ids -> targetRepository.findAll(JpaManagementHelper.combineWithAnd(List.of(
TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, setId),
TargetSpecifications.notEqualToTargetUpdateStatus(TargetUpdateStatus.PENDING))));
return ListUtils.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream().map(mapper).flatMap(List::stream).toList();
}

View File

@@ -23,8 +23,6 @@ import org.eclipse.hawkbit.context.AccessContext;
import org.eclipse.hawkbit.repository.QuotaManagement;
import org.eclipse.hawkbit.repository.RepositoryProperties;
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent;
import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent;
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
@@ -54,19 +52,14 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
OnlineDsAssignmentStrategy(
final TargetRepository targetRepository,
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
final QuotaManagement quotaManagement, final BooleanSupplier multiAssignmentsConfig,
final QuotaManagement quotaManagement,
final BooleanSupplier confirmationFlowConfig, final RepositoryProperties repositoryProperties,
final Consumer<MaxAssignmentsExceededInfo> maxAssignmentExceededHandler) {
super(targetRepository, actionRepository, actionStatusRepository,
quotaManagement, multiAssignmentsConfig, confirmationFlowConfig, repositoryProperties, maxAssignmentExceededHandler);
quotaManagement, confirmationFlowConfig, repositoryProperties, maxAssignmentExceededHandler);
}
public void sendDeploymentEvents(final long distributionSetId, final List<Action> actions) {
if (isMultiAssignmentsEnabled()) {
sendDeploymentEvent(actions);
return;
}
final List<Action> filteredActions = getActionsWithoutCancellations(actions);
if (filteredActions.isEmpty()) {
return;
@@ -106,13 +99,9 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
@Override
public List<JpaTarget> findTargetsForAssignment(final List<String> controllerIDs, final long setId) {
final Function<List<String>, List<JpaTarget>> mapper;
if (isMultiAssignmentsEnabled()) {
mapper = ids -> targetRepository.findAll(TargetSpecifications.hasControllerIdIn(ids));
} else {
mapper = ids -> targetRepository
.findAll(TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, setId));
}
final Function<List<String>, List<JpaTarget>> mapper =
ids -> targetRepository.findAll(
TargetSpecifications.hasControllerIdAndAssignedDistributionSetIdNot(ids, setId));
return ListUtils.partition(controllerIDs, Constants.MAX_ENTRIES_IN_STATEMENT).stream().map(mapper)
.flatMap(List::stream).toList();
}
@@ -163,27 +152,15 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
@Override
public void sendDeploymentEvents(final List<DistributionSetAssignmentResult> assignmentResults) {
if (isMultiAssignmentsEnabled()) {
sendDeploymentEvent(assignmentResults.stream().flatMap(result -> result.getAssignedEntity().stream()).toList());
} else {
assignmentResults.forEach(this::sendDistributionSetAssignedEvent);
}
assignmentResults.forEach(this::sendDistributionSetAssignedEvent);
}
void sendCancellationMessage(final JpaAction action) {
if (isMultiAssignmentsEnabled()) {
sendMultiActionCancelEvent(action);
} else {
cancelAssignDistributionSetEvent(action);
}
cancelAssignDistributionSetEvent(action);
}
void sendCancellationMessages(final List<JpaAction> actions, final String tenant) {
if (isMultiAssignmentsEnabled()) {
sendMultiActionCancelEvent(tenant, Collections.unmodifiableList(actions));
} else {
actions.forEach(this::cancelAssignDistributionSetEvent);
}
void sendCancellationMessages(final List<JpaAction> actions) {
actions.forEach(this::cancelAssignDistributionSetEvent);
}
private static Stream<Action> filterCancellations(final List<Action> actions) {
@@ -200,19 +177,6 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
return filterCancellations(actions).toList();
}
private void sendMultiActionCancelEvent(final Action action) {
sendMultiActionCancelEvent(action.getTenant(), Collections.singletonList(action));
}
private void sendDeploymentEvent(final List<Action> actions) {
final List<Action> filteredActions = getActionsWithoutCancellations(actions);
if (filteredActions.isEmpty()) {
return;
}
final String tenant = filteredActions.get(0).getTenant();
sendMultiActionAssignEvent(tenant, filteredActions);
}
private void sendDistributionSetAssignedEvent(final DistributionSetAssignmentResult assignmentResult) {
final List<Action> filteredActions = filterCancellations(assignmentResult.getAssignedEntity()).toList();
final DistributionSet set = assignmentResult.getDistributionSet();
@@ -228,25 +192,4 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
new TargetAssignDistributionSetEvent(tenant, distributionSetId, actions, actions.get(0).isMaintenanceWindowAvailable())));
}
/**
* Helper to fire a {@link MultiActionCancelEvent}. This method may only be
* called if the Multi-Assignments feature is enabled.
*
* @param tenant the event is scoped to
* @param actions assigned to the targets
*/
private void sendMultiActionCancelEvent(final String tenant, final List<Action> actions) {
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new MultiActionCancelEvent(tenant, actions)));
}
/**
* Helper to fire a {@link MultiActionAssignEvent}. This method may only be called if the Multi-Assignments feature is enabled.
*
* @param tenant the event is scoped to
* @param actions assigned to the targets
*/
private void sendMultiActionAssignEvent(final String tenant, final List<Action> actions) {
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new MultiActionAssignEvent(tenant, actions)));
}
}

View File

@@ -84,8 +84,7 @@ public final class WeightValidationHelper {
public static void validateWeight(final boolean hasWeight, final boolean hasNoWeight) {
// remove bypassing the weight enforcement as soon as weight can be set via UI
final boolean bypassWeightEnforcement = true;
final boolean multiAssignmentsEnabled = TenantConfigHelper.isMultiAssignmentsEnabled();
if (!bypassWeightEnforcement && multiAssignmentsEnabled && hasNoWeight) {
if (!bypassWeightEnforcement && hasNoWeight) {
throw new NoWeightProvidedInMultiAssignmentModeException();
}
}