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();
}
}

View File

@@ -30,44 +30,6 @@ class RemoteTenantEventTest extends AbstractRemoteEventTest {
private static final String TENANT_DEFAULT = "DEFAULT";
/**
* Verifies that a testMultiActionAssignEvent can be properly serialized and deserialized
*/
@Test
void testMultiActionAssignEvent() {
final List<String> controllerIds = List.of("id0", "id1", "id2", "id3", "id4loooooooooooooooooooooooooooooooooooonnnnnnnnnnnnnnnnnng");
final List<Action> actions = controllerIds.stream().map(this::createAction).toList();
final MultiActionAssignEvent assignEvent = new MultiActionAssignEvent(TENANT_DEFAULT, actions);
final MultiActionAssignEvent remoteAssignEventProtoStuff = createProtoStuffEvent(assignEvent);
assertThat(assignEvent).isEqualTo(remoteAssignEventProtoStuff);
assertThat(remoteAssignEventProtoStuff.getControllerIds()).containsExactlyElementsOf(controllerIds);
final MultiActionAssignEvent remoteAssignEventJackson = createJacksonEvent(assignEvent);
assertThat(assignEvent).isEqualTo(remoteAssignEventJackson);
assertThat(remoteAssignEventJackson.getControllerIds()).containsExactlyElementsOf(controllerIds);
}
/**
* Verifies that a MultiActionCancelEvent can be properly serialized and deserialized
*/
@Test
void testMultiActionCancelEvent() {
final List<String> controllerIds = List.of("id0", "id1", "id2", "id3", "id4loooooooooooooooooooooooooooooooooooonnnnnnnnnnnnnnnnnng");
final List<Action> actions = controllerIds.stream().map(this::createAction).toList();
final MultiActionCancelEvent cancelEvent = new MultiActionCancelEvent(TENANT_DEFAULT, actions);
final MultiActionCancelEvent remoteCancelEventProtoStuff = createProtoStuffEvent(cancelEvent);
assertThat(cancelEvent).isEqualTo(remoteCancelEventProtoStuff);
assertThat(remoteCancelEventProtoStuff.getControllerIds()).containsExactlyElementsOf(controllerIds);
final MultiActionCancelEvent remoteCancelEventJackson = createJacksonEvent(cancelEvent);
assertThat(cancelEvent).isEqualTo(remoteCancelEventJackson);
assertThat(remoteCancelEventJackson.getControllerIds()).containsExactlyElementsOf(controllerIds);
}
/**
* Verifies that a DownloadProgressEvent can be properly serialized and deserialized
*/
@@ -135,15 +97,6 @@ class RemoteTenantEventTest extends AbstractRemoteEventTest {
assertCancelTargetAssignmentEvent(action, remoteEventJackson);
}
private Action createAction(final String controllerId) {
final JpaAction generateAction = new JpaAction();
generateAction.setId(1L);
generateAction.setActionType(ActionType.FORCED);
generateAction.setTarget(testdataFactory.createTarget(controllerId));
generateAction.setStatus(Status.RUNNING);
return generateAction;
}
private void assertTargetAssignDistributionSetEvent(final Action action, final TargetAssignDistributionSetEvent underTest) {
assertThat(underTest.getActions()).hasSize(1);
final ActionProperties actionProperties = underTest.getActions().get(action.getTarget().getControllerId());

View File

@@ -16,7 +16,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.List;
import java.util.Set;
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
@@ -25,7 +24,6 @@ import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.service.CancelTargetAssignmentServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.MultiActionAssignServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.TargetAssignDistributionSetServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.TargetAttributesRequestedServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.TargetCreatedServiceEvent;
@@ -79,8 +77,6 @@ class ServiceEventsTest {
TargetAssignDistributionSetEvent.class,
CancelTargetAssignmentEvent.class,
TargetAttributesRequestedEvent.class,
MultiActionAssignEvent.class,
MultiActionCancelEvent.class,
ActionCreatedEvent.class,
ActionUpdatedEvent.class
);
@@ -124,16 +120,6 @@ class ServiceEventsTest {
verify(streamBridge).send(eq("group"), any(TargetAttributesRequestedServiceEvent.class));
}
@Test
void testProcessingMultiActionAssignmentEventIsSent() {
MultiActionAssignEvent event = new MultiActionAssignEvent("testtenant", List.of(mockAction()));
publisher.publishEvent(event);
verify(streamBridge).send("fanout", event);
verify(streamBridge).send(eq("group"), any(MultiActionAssignServiceEvent.class));
}
@Test
void testCancelTargetAssignmentEventIsSent() {
CancelTargetAssignmentEvent event = new CancelTargetAssignmentEvent(mockAction());

View File

@@ -64,37 +64,6 @@ class ConfirmationManagementTest extends AbstractJpaIntegrationTest {
.allMatch(action -> action.getStatus() == Status.WAIT_FOR_CONFIRMATION);
}
/**
* Verify 'findActiveActionsWaitingConfirmation' method is filtering like expected with multi assignment active
*/
@Test
void retrieveActionsWithConfirmationStateInMultiAssignment() {
enableMultiAssignments();
enableConfirmationFlow();
final String controllerId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
final List<Action> actions = assignDistributionSet(dsId, controllerId).getAssignedEntity();
assertThat(actions).hasSize(1);
assertThat(confirmationManagement.findActiveActionsWaitingConfirmation(controllerId)).hasSize(1)
.allMatch(action -> action.getStatus() == Status.WAIT_FOR_CONFIRMATION);
final Long dsId2 = testdataFactory.createDistributionSet().getId();
assignDistributionSet(dsId2, controllerId);
assertThat(confirmationManagement.findActiveActionsWaitingConfirmation(controllerId)).hasSize(2)
.allMatch(action -> action.getStatus() == Status.WAIT_FOR_CONFIRMATION);
confirmationManagement.confirmAction(actions.get(0).getId(), null, null);
assertThat(confirmationManagement.findActiveActionsWaitingConfirmation(controllerId)).hasSize(1)
.allMatch(action -> action.getStatus() == Status.WAIT_FOR_CONFIRMATION
&& Objects.equals(action.getDistributionSet().getId(), dsId2));
}
/**
* Verify confirming an action will put it to the running state
*/
@@ -191,34 +160,6 @@ class ConfirmationManagementTest extends AbstractJpaIntegrationTest {
.noneMatch(status -> status.getStatus() == Status.RUNNING);
}
/**
* Verify multiple actions in WFC state will be transferred in RUNNING state in case auto-confirmation is activated.
*/
@Test
void activateAutoConfirmationInMultiAssignment() {
enableMultiAssignments();
enableConfirmationFlow();
final String controllerId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
final Long dsId2 = testdataFactory.createDistributionSet().getId();
final List<Action> actions = assignDistributionSets(
Arrays.asList(toDeploymentRequest(controllerId, dsId), toDeploymentRequest(controllerId, dsId2)))
.stream().flatMap(s -> s.getAssignedEntity().stream()).toList();
assertThat(actions).hasSize(2);
assertThat(confirmationManagement.findActiveActionsWaitingConfirmation(controllerId)).hasSize(2)
.allMatch(action -> action.getStatus() == Status.WAIT_FOR_CONFIRMATION);
confirmationManagement.activateAutoConfirmation(controllerId, null, null);
assertThat(confirmationManagement.findActiveActionsWaitingConfirmation(controllerId)).isEmpty();
assertThat(deploymentManagement.findActionsByTarget(controllerId, PAGE).getContent()).hasSize(2)
.allMatch(action -> action.getStatus() == Status.RUNNING);
}
/**
* Verify action in WFC state will be transferred in RUNNING state in case auto-confirmation is activated.
*/
@@ -324,10 +265,6 @@ class ConfirmationManagementTest extends AbstractJpaIntegrationTest {
Arguments.of(null, null));
}
private static DeploymentRequest toDeploymentRequest(final String controllerId, final Long distributionSetId) {
return DeploymentRequest.builder(controllerId, distributionSetId).confirmationRequired(true).build();
}
private void verifyAutoConfirmationIsDisabled(final String controllerId) {
assertThat(targetManagement.getWithAutoConfigurationStatus(controllerId).getAutoConfirmationStatus()).isNull();
}

View File

@@ -1582,41 +1582,6 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertNoActiveActionsExistsForControllerId(DEFAULT_CONTROLLER_ID);
}
/**
* Actions are exposed according to thier weight in multi assignment mode.
*/
@Test
void actionsAreExposedAccordingToTheirWeight() {
final String targetId = testdataFactory.createTarget().getControllerId();
final DistributionSet ds = testdataFactory.createDistributionSet();
final Long actionWeightNull = assignDistributionSet(ds.getId(), targetId).getAssignedEntity().get(0).getId();
enableMultiAssignments();
final Long actionWeight500old = assignDistributionSet(ds.getId(), targetId, 500).getAssignedEntity().get(0)
.getId();
final Long actionWeight500new = assignDistributionSet(ds.getId(), targetId, 500).getAssignedEntity().get(0)
.getId();
final Long actionWeight1000 = assignDistributionSet(ds.getId(), targetId, 1000).getAssignedEntity().get(0)
.getId();
assertThat(controllerManagement.findActiveActionWithHighestWeight(targetId).get().getId())
.isEqualTo(actionWeightNull);
controllerManagement
.addUpdateActionStatus(ActionStatusCreate.builder().actionId(actionWeightNull).status(Status.FINISHED).build());
assertThat(controllerManagement.findActiveActionWithHighestWeight(targetId).get().getId())
.isEqualTo(actionWeight1000);
controllerManagement
.addUpdateActionStatus(ActionStatusCreate.builder().actionId(actionWeight1000).status(Status.FINISHED).build());
assertThat(controllerManagement.findActiveActionWithHighestWeight(targetId).get().getId())
.isEqualTo(actionWeight500old);
controllerManagement
.addUpdateActionStatus(ActionStatusCreate.builder().actionId(actionWeight500old).status(Status.FINISHED).build());
assertThat(controllerManagement.findActiveActionWithHighestWeight(targetId).get().getId())
.isEqualTo(actionWeight500new);
controllerManagement
.addUpdateActionStatus(ActionStatusCreate.builder().actionId(actionWeight500new).status(Status.FINISHED).build());
assertThat(controllerManagement.findActiveActionWithHighestWeight(targetId)).isEmpty();
}
/**
* Delete a target on requested target deletion from client side
*/

View File

@@ -37,8 +37,6 @@ import org.eclipse.hawkbit.repository.DistributionSetManagement;
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent;
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.event.remote.entity.ActionCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.ActionUpdatedEvent;
@@ -50,13 +48,18 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TenantConfigurationCreatedEvent;
import org.eclipse.hawkbit.repository.event.remote.entity.TenantConfigurationUpdatedEvent;
import org.eclipse.hawkbit.repository.event.remote.service.ActionCreatedServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.ActionUpdatedServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.CancelTargetAssignmentServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.TargetAssignDistributionSetServiceEvent;
import org.eclipse.hawkbit.repository.event.remote.service.TargetUpdatedServiceEvent;
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.exception.ForceQuitActionNotAllowedException;
import org.eclipse.hawkbit.repository.exception.IncompatibleTargetTypeException;
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
import org.eclipse.hawkbit.repository.exception.MultiAssignmentException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
@@ -208,19 +211,22 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
*/
@Test
void assertMaxActionsPerTargetQuotaIsEnforced() {
enableMultiAssignments();
final int maxActions = quotaManagement.getMaxActionsPerTarget();
final Target testTarget = testdataFactory.createTarget();
final Long ds1Id = testdataFactory.createDistributionSet("ds1").getId();
final List<Long> dsIds = new ArrayList<>();
for (int i = 0; i < maxActions; i++) {
dsIds.add(testdataFactory.createDistributionSet("ds" + i).getId());
}
final String controllerId = testTarget.getControllerId();
for (int i = 0; i < maxActions; i++) {
deploymentManagement.offlineAssignedDistributionSets(List.of(new SimpleEntry<>(controllerId, ds1Id)));
deploymentManagement.offlineAssignedDistributionSets(List.of(new SimpleEntry<>(controllerId, dsIds.get(i))));
}
final long dsIdThatShouldNotBeAssigned = testdataFactory.createDistributionSet("shouldNotBeAssigned").getId();
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> assignDistributionSet(ds1Id, controllerId, 77));
.isThrownBy(() -> assignDistributionSet(dsIdThatShouldNotBeAssigned, controllerId, 77));
}
/**
@@ -539,44 +545,6 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
.allMatch(target -> target.getLastModifiedAt() == target.getInstallationDate());
}
/**
* Offline assign multiple DSs to a single Target in multiassignment mode.
*/
@Test
@ExpectEvents({
@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 4),
@Expect(type = ActionCreatedEvent.class, count = 4),
@Expect(type = DistributionSetCreatedEvent.class, count = 4),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 12),
@Expect(type = DistributionSetUpdatedEvent.class, count = 4), // implicit lock
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 12), // implicit lock
@Expect(type = TenantConfigurationCreatedEvent.class, count = 1) })
void multiOfflineAssignment() {
final List<String> targetIds = testdataFactory.createTargets(1).stream().map(Target::getControllerId).toList();
final List<Long> dsIds = testdataFactory.createDistributionSets(4).stream().map(DistributionSet::getId).toList();
enableMultiAssignments();
final List<Entry<String, Long>> offlineAssignments = new ArrayList<>();
targetIds.forEach(targetId -> dsIds.forEach(dsId -> offlineAssignments.add(new SimpleEntry<>(targetId, dsId))));
final List<DistributionSetAssignmentResult> assignmentResults = deploymentManagement
.offlineAssignedDistributionSets(offlineAssignments);
assertThat(getResultingActionCount(assignmentResults)).isEqualTo(4);
targetIds.forEach(controllerId -> {
final List<Long> assignedDsIds = deploymentManagement.findActionsByTarget(controllerId, PAGE).stream()
.map(a -> {
// don't use peek since it is by documentation mainly for debugging and could be skipped in some cases
assertThat(a.getInitiatedBy())
.as("Actions should be initiated by current user")
.isEqualTo(AccessContext.actor());
return a;
})
.map(action -> action.getDistributionSet().getId()).toList();
assertThat(assignedDsIds).containsExactlyInAnyOrderElementsOf(dsIds);
});
}
/**
* Verifies that if an account is set to action autoclose running actions in case of a new assigned set get closed and set to CANCELED.
*/
@@ -620,118 +588,11 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
}
}
/**
* If multi-assignment is enabled, verify that the previous Distribution Set assignment is not canceled when a new one is assigned.
*/
@Test
@ExpectEvents({
@Expect(type = TargetCreatedEvent.class, count = 10),
@Expect(type = TargetUpdatedEvent.class, count = 20),
@Expect(type = ActionCreatedEvent.class, count = 20),
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
@Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), // implicit lock
@Expect(type = MultiActionAssignEvent.class, count = 2),
@Expect(type = TenantConfigurationCreatedEvent.class, count = 1) })
void previousAssignmentsAreNotCanceledInMultiAssignMode() {
enableMultiAssignments();
final List<Target> targets = testdataFactory.createTargets(10);
final List<String> targetIds = targets.stream().map(Target::getControllerId).toList();
// First assignment
final DistributionSet ds1 = testdataFactory.createDistributionSet("Multi-assign-1");
assignDistributionSet(ds1.getId(), targetIds, 77);
assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_ACTIVE, RUNNING);
// Second assignment
final DistributionSet ds2 = testdataFactory.createDistributionSet("Multi-assign-2");
assignDistributionSet(ds2.getId(), targetIds, 45);
assertDsExclusivelyAssignedToTargets(targets, ds2.getId(), STATE_ACTIVE, RUNNING);
assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_ACTIVE, RUNNING);
}
/**
* Assign multiple DSs to a single Target in one request in multiassignment mode.
*/
@Test
@ExpectEvents({
@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 4),
@Expect(type = ActionCreatedEvent.class, count = 4),
@Expect(type = DistributionSetCreatedEvent.class, count = 4),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 12),
@Expect(type = DistributionSetUpdatedEvent.class, count = 4), // implicit lock
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 12), // implicit lock
@Expect(type = MultiActionAssignEvent.class, count = 1),
@Expect(type = TenantConfigurationCreatedEvent.class, count = 1) })
void multiAssignmentInOneRequest() {
final List<Target> targets = testdataFactory.createTargets(1);
final List<DistributionSet> distributionSets = testdataFactory.createDistributionSets(4);
final List<DeploymentRequest> deploymentRequests = createAssignmentRequests(distributionSets, targets, 34);
enableMultiAssignments();
final List<DistributionSetAssignmentResult> results = deploymentManagement.assignDistributionSets(deploymentRequests, null);
assertThat(getResultingActionCount(results)).isEqualTo(deploymentRequests.size());
final List<Long> dsIds = distributionSets.stream().map(DistributionSet::getId).toList();
targets.forEach(target -> {
final List<Long> assignedDsIds = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE)
.stream()
.map(a -> {
// don't use peek since it is by documentation mainly for debugging and could be skipped in some cases
assertThat(a.getInitiatedBy()).as("Initiated by current user")
.isEqualTo(AccessContext.actor());
return a;
})
.map(action -> action.getDistributionSet().getId()).toList();
assertThat(assignedDsIds).containsExactlyInAnyOrderElementsOf(dsIds);
});
}
/**
* Assign multiple DSs to single Target in one request in multiAssignment mode and cancel each created action afterwards.
*/
@Test
@ExpectEvents({
@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 4),
@Expect(type = ActionCreatedEvent.class, count = 4),
@Expect(type = DistributionSetCreatedEvent.class, count = 4),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 12),
@Expect(type = DistributionSetUpdatedEvent.class, count = 4), // implicit lock
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 12), // implicit lock
@Expect(type = MultiActionAssignEvent.class, count = 1),
@Expect(type = MultiActionCancelEvent.class, count = 4),
@Expect(type = ActionUpdatedEvent.class, count = 4),
@Expect(type = TenantConfigurationCreatedEvent.class, count = 1) })
void cancelMultiAssignmentActions() {
final List<Target> targets = testdataFactory.createTargets(1);
final List<DistributionSet> distributionSets = testdataFactory.createDistributionSets(4);
final List<DeploymentRequest> deploymentRequests = createAssignmentRequests(distributionSets, targets, 34, false);
enableMultiAssignments();
final List<DistributionSetAssignmentResult> results = deploymentManagement.assignDistributionSets(deploymentRequests, null);
assertThat(getResultingActionCount(results)).isEqualTo(deploymentRequests.size());
final List<Long> dsIds = distributionSets.stream().map(DistributionSet::getId).toList();
targets.forEach(target ->
deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).forEach(action -> {
assertThat(action.getDistributionSet().getId()).isIn(dsIds);
assertThat(action.getInitiatedBy())
.as("Should be Initiated by current user").isEqualTo(AccessContext.actor());
deploymentManagement.cancelAction(action.getId());
}));
}
/**
* A Request resulting in multiple assignments to a single target is only allowed when multiassignment is enabled.
*/
@Test
void multipleAssignmentsToTargetOnlyAllowedInMultiAssignMode() {
void multipleAssignmentsToTargetNotAllowed() {
final Target target = testdataFactory.createTarget();
final List<DistributionSet> distributionSets = testdataFactory.createDistributionSets(2);
@@ -742,11 +603,9 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
.builder(target.getControllerId(), distributionSets.get(1).getId()).weight(565).build();
final List<DeploymentRequest> deploymentRequests = List.of(targetToDS0, targetToDS1);
Assertions.assertThatExceptionOfType(MultiAssignmentIsNotEnabledException.class)
Assertions.assertThatExceptionOfType(MultiAssignmentException.class)
.isThrownBy(() -> deploymentManagement.assignDistributionSets(deploymentRequests, null));
enableMultiAssignments();
assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(List.of(targetToDS0, targetToDS1), null))).isEqualTo(2);
}
/**
@@ -912,77 +771,46 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
}));
}
/**
* Duplicate Assignments are removed from a request when multi-assignment is disabled, otherwise not
*/
@Test
@ExpectEvents({
@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 2),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = MultiActionAssignEvent.class, count = 1),
@Expect(type = TenantConfigurationCreatedEvent.class, count = 1) })
void duplicateAssignmentsInRequestAreRemovedIfMultiassignmentEnabled() {
final String targetId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
final List<DeploymentRequest> twoEqualAssignments = Collections.nCopies(2, DeploymentRequest.builder(targetId, dsId).build());
assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(twoEqualAssignments, null))).isEqualTo(1);
enableMultiAssignments();
final List<DeploymentRequest> twoEqualAssignmentsWithWeight = Collections.nCopies(
2, DeploymentRequest.builder(targetId, dsId).weight(555).build());
assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(twoEqualAssignmentsWithWeight, null))).isEqualTo(1);
}
/**
* An assignment request is not accepted if it would lead to a target exceeding the max actions per target quota.
*/
@Test
@ExpectEvents({
@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 20),
@Expect(type = TargetUpdatedServiceEvent.class, count = 20),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 20),
@Expect(type = CancelTargetAssignmentEvent.class, count = 19),
@Expect(type = CancelTargetAssignmentServiceEvent.class, count = 19),
@Expect(type = DistributionSetCreatedEvent.class, count = 21), // max actions per target are 20 for test
@Expect(type = DistributionSetUpdatedEvent.class, count = 20),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3 * 21),
@Expect(type = TenantConfigurationCreatedEvent.class, count = 1) })
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 3 * 20),
@Expect(type = ActionUpdatedEvent.class, count = 19),
@Expect(type = ActionCreatedEvent.class, count = 20),
@Expect(type = ActionCreatedServiceEvent.class, count = 20),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 20),
})
void maxActionsPerTargetIsCheckedBeforeAssignmentExecution() {
final int maxActions = quotaManagement.getMaxActionsPerTarget();
assertThat(maxActions)
.as("Expect 20 as maxActionPerTarget. If not the case change @Expect counts for " +
"DistributionSetCreatedEvent and SoftwareModuleCreatedEvent accordingly!")
.isEqualTo(20);
final int size = maxActions + 1;
final String controllerId = testdataFactory.createTarget().getControllerId();
final List<DeploymentRequest> deploymentRequests = new ArrayList<>();
for (int i = 0; i < size; i++) {
for (int i = 0; i < maxActions; i++) {
final Long dsId = testdataFactory.createDistributionSet().getId();
deploymentRequests.add(DeploymentRequest.builder(controllerId, dsId).weight(24).build());
deploymentManagement.assignDistributionSets(
List.of(DeploymentRequest.builder(controllerId, dsId).weight(24).build()), null);
}
enableMultiAssignments();
final Long dsId = testdataFactory.createDistributionSet().getId();
final List<DeploymentRequest> deploymentRequests = List.of(DeploymentRequest.builder(controllerId, dsId).weight(24).build());
Assertions.assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> deploymentManagement.assignDistributionSets(deploymentRequests, null));
assertThat(actionRepository.countByTargetControllerId(controllerId)).isZero();
}
/**
* An assignment request without a weight is ok when multi assignment in enabled.
*/
@Test
void weightNotRequiredInMultiAssignmentMode() {
final String targetId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
final DeploymentRequest assignWithoutWeight = DeploymentRequest.builder(targetId, dsId).build();
final DeploymentRequest assignWithWeight = DeploymentRequest.builder(targetId, dsId).weight(567).build();
enableMultiAssignments();
assertThat(deploymentManagement.assignDistributionSets(List.of(assignWithoutWeight, assignWithWeight), null)).isNotNull();
assertThat(actionRepository.countByTargetControllerId(controllerId)).isEqualTo(20L);
}
/**
@@ -1003,22 +831,28 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
@Test
@ExpectEvents({
@Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 3),
@Expect(type = DistributionSetUpdatedEvent.class, count = 1), // implicit lock
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 3), // implicit lock
@Expect(type = DistributionSetCreatedEvent.class, count = 2),
@Expect(type = SoftwareModuleCreatedEvent.class, count = 6),
@Expect(type = DistributionSetUpdatedEvent.class, count = 2), // implicit lock
@Expect(type = SoftwareModuleUpdatedEvent.class, count = 6), // implicit lock
@Expect(type = ActionCreatedEvent.class, count = 2),
@Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = ActionUpdatedServiceEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = MultiActionAssignEvent.class, count = 2),
@Expect(type = TenantConfigurationCreatedEvent.class, count = 1) })
@Expect(type = TargetAssignDistributionSetEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetServiceEvent.class, count = 2),
@Expect(type = CancelTargetAssignmentEvent.class, count = 1),
@Expect(type = CancelTargetAssignmentServiceEvent.class, count = 1)
})
void weightValidatedAndSaved() {
final String targetId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
final DeploymentRequest validRequest1 = DeploymentRequest.builder(targetId, dsId).weight(Action.WEIGHT_MAX).build();
final DeploymentRequest validRequest2 = DeploymentRequest.builder(targetId, dsId).weight(Action.WEIGHT_MIN).build();
final DeploymentRequest weightTooLow = DeploymentRequest.builder(targetId, dsId).weight(Action.WEIGHT_MIN - 1).build();
final DeploymentRequest weightTooHigh = DeploymentRequest.builder(targetId, dsId).weight(Action.WEIGHT_MAX + 1).build();
enableMultiAssignments();
final Long dsId1 = testdataFactory.createDistributionSet().getId();
final Long dsId2 = testdataFactory.createDistributionSet().getId();
final DeploymentRequest validRequest1 = DeploymentRequest.builder(targetId, dsId1).weight(Action.WEIGHT_MAX).build();
final DeploymentRequest validRequest2 = DeploymentRequest.builder(targetId, dsId2).weight(Action.WEIGHT_MIN).build();
final DeploymentRequest weightTooLow = DeploymentRequest.builder(targetId, dsId1).weight(Action.WEIGHT_MIN - 1).build();
final DeploymentRequest weightTooHigh = DeploymentRequest.builder(targetId, dsId1).weight(Action.WEIGHT_MAX + 1).build();
final List<DeploymentRequest> deploymentRequestsTooLow = Collections.singletonList(weightTooLow);
Assertions.assertThatExceptionOfType(ConstraintViolationException.class)
.isThrownBy(() -> deploymentManagement.assignDistributionSets(deploymentRequestsTooLow, null));
@@ -1027,9 +861,11 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
() -> deploymentManagement.assignDistributionSets(deploymentRequestsTooHigh, null));
final Long validActionId1 = getFirstAssignedAction(
deploymentManagement.assignDistributionSets(Collections.singletonList(validRequest1), null).get(0)).getId();
final Long validActionId2 = getFirstAssignedAction(
deploymentManagement.assignDistributionSets(Collections.singletonList(validRequest2), null).get(0)).getId();
assertThat(actionRepository.findById(validActionId1).get().getWeight()).get().isEqualTo(Action.WEIGHT_MAX);
DistributionSetAssignmentResult assignmentResult = deploymentManagement.assignDistributionSets(Collections.singletonList(validRequest2), null).get(0);
System.out.println(assignmentResult.getAssignedEntity().toString());
final Long validActionId2 = getFirstAssignedAction(assignmentResult).getId();
assertThat(actionRepository.findById(validActionId2).get().getWeight()).get().isEqualTo(Action.WEIGHT_MIN);
}
@@ -1720,20 +1556,6 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
}
private List<DeploymentRequest> createAssignmentRequests(
final Collection<DistributionSet> distributionSets, final Collection<Target> targets, final int weight) {
return createAssignmentRequests(distributionSets, targets, weight, false);
}
private List<DeploymentRequest> createAssignmentRequests(final Collection<DistributionSet> distributionSets,
final Collection<Target> targets, final int weight, final boolean confirmationRequired) {
final List<DeploymentRequest> deploymentRequests = new ArrayList<>();
distributionSets.forEach(ds -> targets.forEach(target -> deploymentRequests.add(
DeploymentRequest.builder(target.getControllerId(), ds.getId())
.weight(weight).confirmationRequired(confirmationRequired).build())));
return deploymentRequests;
}
private JpaAction assignSet(final Target target, final DistributionSet ds) {
assignDistributionSet(ds.getId(), target.getControllerId());
implicitLock(ds);

View File

@@ -25,6 +25,7 @@ import java.util.stream.Stream;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.ValidationException;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.api.Assertions;
import org.assertj.core.api.Condition;
import org.eclipse.hawkbit.auth.SpPermission;
@@ -1994,18 +1995,6 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
assertThat(rolloutsOrderedByName).containsSubsequence(List.of(rolloutRunning, rolloutReady));
}
/**
* Creating a rollout without weight value when multi assignment in enabled.
*/
@Test
void weightNotRequiredInMultiAssignmentMode() {
enableMultiAssignments();
final Rollout rollout = testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet(10, 10, 2, "50",
"80",
ActionType.FORCED, null);
assertThat(rollout).isNotNull();
}
/**
* Creating a rollout with a weight causes an error when multi assignment in disabled.
*/
@@ -2024,7 +2013,6 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
void weightValidatedAndSaved() {
final String targetPrefix = UUID.randomUUID().toString();
testdataFactory.createTargets(4, targetPrefix);
enableMultiAssignments();
final String rolloutName = UUID.randomUUID().toString();
final String targetPrefixName = UUID.randomUUID().toString();
@@ -2054,7 +2042,6 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
void actionsWithWeightAreCreated() {
final int amountOfTargets = 5;
final int weight = 99;
enableMultiAssignments();
final Long rolloutId = testdataFactory
.createSimpleTestRolloutWithTargetsAndDistributionSet(amountOfTargets, 2, amountOfTargets,
"80", "50", null, weight).getId();
@@ -2066,24 +2053,6 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
.allMatch(action -> action.getWeight().get() == weight);
}
/**
* Rollout can be created without weight in single assignment and be started in multi assignment
*/
@Test
void createInSingleStartInMultiassignMode() {
final int amountOfTargets = 5;
final Long rolloutId = testdataFactory.createSimpleTestRolloutWithTargetsAndDistributionSet(amountOfTargets, 2,
amountOfTargets,
"80", "50", null, null).getId();
enableMultiAssignments();
rolloutManagement.start(rolloutId);
rolloutHandler.handleAll();
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
// wight replaced with default
assertThat(actions).hasSize(5).allMatch(action -> action.getWeight().isPresent());
}
/**
* Verifies that an exception is thrown when trying to create a rollout with an invalidated distribution set.
*/

View File

@@ -244,23 +244,6 @@ class TargetFilterQueryManagementTest extends AbstractRepositoryManagementTest<T
verifyFindForAllWithAutoAssignDs(tfq, tfq2);
}
/**
* Creating or updating a target filter query with autoassignment and no-value weight when multi assignment in enabled.
*/
@Test
void weightNotRequiredInMultiAssignmentMode() {
enableMultiAssignments();
final DistributionSet ds = testdataFactory.createDistributionSet();
final Long filterId = targetFilterQueryManagement.create(Create.builder().name("a").query("name==*").build()).getId();
assertThat(
targetFilterQueryManagement.create(Create.builder().name("b").query("name==*").autoAssignDistributionSet(ds).build()))
.isNotNull();
assertThat(
targetFilterQueryManagement.updateAutoAssignDS(new AutoAssignDistributionSetUpdate(filterId).ds(ds.getId())))
.isNotNull();
}
/**
* Creating or updating a target filter query with autoassignment with a weight causes an error when multi assignment in disabled.
*/
@@ -279,23 +262,8 @@ class TargetFilterQueryManagementTest extends AbstractRepositoryManagementTest<T
.isNotNull();
}
/**
* Auto assignment can be removed from filter when multi assignment in enabled.
*/
@Test
void removeDsFromFilterWhenMultiAssignmentModeNotEnabled() {
enableMultiAssignments();
final DistributionSet ds = testdataFactory.createDistributionSet();
final Long filterId = targetFilterQueryManagement.create(
Create.builder().name("a").query("name==*").autoAssignDistributionSet(ds).autoAssignWeight(23).build()).getId();
assertThat(targetFilterQueryManagement.updateAutoAssignDS(
new AutoAssignDistributionSetUpdate(filterId).ds(null).weight(null)))
.isNotNull();
}
@Test
void weightValidatedAndSaved() {
enableMultiAssignments();
final DistributionSet ds = testdataFactory.createDistributionSet();
final Create targetFilterQueryCreate = Create.builder().name("a")

View File

@@ -328,7 +328,6 @@ class AutoAssignHandlerIntTest extends AbstractJpaIntegrationTest {
final int amountOfTargets = 5;
final DistributionSet ds = testdataFactory.createDistributionSet();
final int weight = 32;
enableMultiAssignments();
targetFilterQueryManagement.create(Create.builder()
.name("a").query("name==*").autoAssignDistributionSet(ds).autoAssignWeight(weight)
@@ -342,25 +341,6 @@ class AutoAssignHandlerIntTest extends AbstractJpaIntegrationTest {
.allMatch(action -> action.getWeight().get() == weight);
}
/**
* An auto assignment target filter without weight still works after multi assignment is enabled
*/
@Test
void filterWithoutWeightWorksInMultiAssignmentMode() {
final int amountOfTargets = 5;
final DistributionSet ds = testdataFactory.createDistributionSet();
targetFilterQueryManagement.create(Create.builder().name("a").query("name==*").autoAssignDistributionSet(ds).build());
enableMultiAssignments();
testdataFactory.createTargets(amountOfTargets);
autoAssignChecker.handleAll();
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
assertThat(actions)
.hasSize(amountOfTargets)
.allMatch(action -> action.getWeight().isPresent());
}
/**
* Verifies an auto assignment only creates actions for compatible targets
*/