Support user consent flow (#1293)

* Introduce user consent flow
* Add permissions to confirmation management
* rename from consent to confirmation
* Reformat code. Remove unused imports. Change and add permission checks when configuring auto-confirmation.
* Do not include null values for DDI confirmation base endpoint
* fix confirmation required checkbox id
* Remove unused import. Fix consume/produce type of new API's.
* Change term processing to proceeding when activating user consent flow
* Align formatting and extend integration test cases for DMF and DDI.
* Extend DMF test cases to consider auto-confirmation
* Refactor action management to fix problem of handling action status updates on closed actions.
* remove unsupported validation
* use new confirmation api for DMF. Extend test cases.,
* Remove unnecessary fields.
* Extend API documentation for DDI and MGMT API.
* adapt ddi api docs adoc file
* Fixed the duplicate migration version for db files
* fix method to support confirmation
* Fixed PR comments
* Addressed PR comments
* Fixed after merge compilation issue
* Fixed after merge compilation issue
* Fix failing tests in MgmtRolloutResourceTest
* Fixed the permissions issue reflected by integration tests
* Added back the missing line of code lost during merge
* Fix the failing test on Jenkins

Signed-off-by: Stanislav Trailov <stanislav.trailov@bosch.io>
Signed-off-by: Dimitar Shterev <dimitar.shterev@bosch.io>
Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io>
Signed-off-by: Shruthi Manavalli Ramanna <shruthimanavalli.ramanna@bosch-si.com>
Co-authored-by: Shruthi Manavalli Ramanna <shruthimanavalli.ramanna@bosch-si.com>
This commit is contained in:
Michael Herdt
2023-01-25 12:11:05 +01:00
committed by GitHub
parent b919ceda5c
commit 21f1569881
208 changed files with 7830 additions and 831 deletions

View File

@@ -44,8 +44,8 @@ public class RolloutEventTest extends AbstractRemoteEntityEventTest<Rollout> {
.type("os").modules(Collections.singletonList(module.getId())));
return rolloutManagement.create(
entityFactory.rollout().create().name("exampleRollout").targetFilterQuery("controllerId==*").set(ds),
5, new RolloutGroupConditionBuilder().withDefaults()
entityFactory.rollout().create().name("exampleRollout").targetFilterQuery("controllerId==*").set(ds), 5,
false, new RolloutGroupConditionBuilder().withDefaults()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "10").build());
}

View File

@@ -84,8 +84,8 @@ public class RolloutGroupEventTest extends AbstractRemoteEntityEventTest<Rollout
.type("os").modules(Collections.singletonList(module.getId())));
final Rollout entity = rolloutManagement.create(
entityFactory.rollout().create().name("exampleRollout").targetFilterQuery("controllerId==*").set(ds),
5, new RolloutGroupConditionBuilder().withDefaults()
entityFactory.rollout().create().name("exampleRollout").targetFilterQuery("controllerId==*").set(ds), 5,
false, new RolloutGroupConditionBuilder().withDefaults()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "10").build());
return rolloutGroupManagement.findByRollout(PAGE, entity.getId()).getContent().get(0);

View File

@@ -117,7 +117,7 @@ public class ConcurrentDistributionSetInvalidationTest extends AbstractJpaIntegr
return rolloutManagement.create(entityFactory.rollout().create()
.name("verifyInvalidateDistributionSetWithLargeRolloutThrowsException").description("desc")
.targetFilterQuery("name==*").set(distributionSet).actionType(ActionType.FORCED),
quotaManagement.getMaxRolloutGroupsPerRollout(), conditions);
quotaManagement.getMaxRolloutGroupsPerRollout(), false, conditions);
}
}

View File

@@ -0,0 +1,315 @@
/**
* Copyright (c) 2022 Bosch.IO GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.hawkbit.repository.jpa;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.hawkbit.repository.exception.AutoConfirmationAlreadyActiveException;
import org.eclipse.hawkbit.repository.exception.InvalidConfirmationFeedbackException;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
import org.eclipse.hawkbit.repository.model.DeploymentRequestBuilder;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.junit.jupiter.api.Test;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.qameta.allure.Story;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
/**
* Test class testing the functionality of triggering a deployment of
* {@link DistributionSet}s to {@link Target}s with AutoConfirmation active.
*
*/
@Feature("Component Tests - Repository")
@Story("Confirmation Management")
class ConfirmationManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Verify 'findActiveActionsWaitingConfirmation' method is filtering like expected")
void retrieveActionsWithConfirmationState() {
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();
// ds1 will be in canceling state afterwards
assignDistributionSet(dsId2, controllerId);
assertThat(confirmationManagement.findActiveActionsWaitingConfirmation(controllerId)).hasSize(1)
.allMatch(action -> action.getStatus() == Status.WAIT_FOR_CONFIRMATION);
}
@Test
@Description("Verify 'findActiveActionsWaitingConfirmation' method is filtering like expected with multi assignment active")
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));
}
@Test
@Description("Verify confirming an action will put it to the running state")
void confirmedActionWillSwitchToRunningState() {
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).allMatch(action -> action.getStatus() == Status.WAIT_FOR_CONFIRMATION);
assertThat(confirmationManagement.findActiveActionsWaitingConfirmation(controllerId)).hasSize(1)
.allMatch(action -> action.getStatus() == Status.WAIT_FOR_CONFIRMATION);
assertThat(controllerManagement.findActionStatusByAction(PAGE, actions.get(0).getId())).hasSize(1)
.allMatch(status -> status.getStatus() == Status.WAIT_FOR_CONFIRMATION);
final Action newAction = confirmationManagement.confirmAction(actions.get(0).getId(), null, null);
assertThat(confirmationManagement.findActiveActionsWaitingConfirmation(controllerId)).isEmpty();
// verify action in RUNNING state
assertThat(newAction.getStatus()).isEqualTo(Status.RUNNING);
// status entry RUNNING should be present in status history
assertThat(controllerManagement.findActionStatusByAction(PAGE, newAction.getId())).hasSize(2)
.anyMatch(status -> status.getStatus() == Status.RUNNING);
}
@Test
@Description("Verify confirming an confirmed action will lead to a specific failure")
void confirmedActionCannotBeConfirmedAgain() {
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).allMatch(action -> action.getStatus() == Status.WAIT_FOR_CONFIRMATION);
final Action newAction = confirmationManagement.confirmAction(actions.get(0).getId(), null, null);
// verify action in RUNNING state
assertThat(newAction.getStatus()).isEqualTo(Status.RUNNING);
assertThatThrownBy(() -> confirmationManagement.confirmAction(actions.get(0).getId(), null, null))
.isInstanceOf(InvalidConfirmationFeedbackException.class)
.matches(e -> ((InvalidConfirmationFeedbackException) e)
.getReason() == InvalidConfirmationFeedbackException.Reason.NOT_AWAITING_CONFIRMATION);
}
@Test
@Description("Verify confirming a closed action will lead to a specific failure")
void confirmedActionCannotBeGivenOnFinishedAction() {
enableConfirmationFlow();
final Action action = prepareFinishedUpdate();
assertThatThrownBy(() -> confirmationManagement.confirmAction(action.getId(), null, null))
.isInstanceOf(InvalidConfirmationFeedbackException.class)
.matches(e -> ((InvalidConfirmationFeedbackException) e)
.getReason() == InvalidConfirmationFeedbackException.Reason.ACTION_CLOSED);
}
@Test
@Description("Verify denying an action will leave it in WFC state")
void deniedActionWillStayInWfcState() {
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).allMatch(action -> action.getStatus() == Status.WAIT_FOR_CONFIRMATION);
assertThat(confirmationManagement.findActiveActionsWaitingConfirmation(controllerId)).hasSize(1)
.allMatch(action -> action.getStatus() == Status.WAIT_FOR_CONFIRMATION);
assertThat(controllerManagement.findActionStatusByAction(PAGE, actions.get(0).getId())).hasSize(1)
.allMatch(status -> status.getStatus() == Status.WAIT_FOR_CONFIRMATION);
final Action newAction = confirmationManagement.denyAction(actions.get(0).getId(), null, null);
assertThat(confirmationManagement.findActiveActionsWaitingConfirmation(controllerId)).hasSize(1)
.allMatch(action -> action.getStatus() == Status.WAIT_FOR_CONFIRMATION);
// verify action still in WFC state
assertThat(newAction.getStatus()).isEqualTo(Status.WAIT_FOR_CONFIRMATION);
// no status entry RUNNING should be present in status history
assertThat(controllerManagement.findActionStatusByAction(PAGE, newAction.getId())).hasSize(2)
.noneMatch(status -> status.getStatus() == Status.RUNNING);
}
@Test
@Description("Verify multiple actions in WFC state will be transferred in RUNNING state in case auto-confirmation is activated.")
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()).collect(Collectors.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);
}
@Test
@Description("Verify action in WFC state will be transferred in RUNNING state in case auto-confirmation is activated.")
void activateAutoConfirmationOnActiveAction() {
enableConfirmationFlow();
final String controllerId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
// do assignment and verify
assertThat(assignDistributionSet(dsId, controllerId).getAssignedEntity()).hasSize(1);
assertThat(confirmationManagement.findActiveActionsWaitingConfirmation(controllerId)).hasSize(1)
.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(1)
.allMatch(action -> action.getStatus() == Status.RUNNING);
}
@Test
@Description("Verify created action after activating auto confirmation is directly in running state.")
void activateAutoConfirmationAndCreateAction() {
enableConfirmationFlow();
final String controllerId = testdataFactory.createTarget().getControllerId();
final Long dsId = testdataFactory.createDistributionSet().getId();
assertThat(confirmationManagement.findActiveActionsWaitingConfirmation(controllerId)).isEmpty();
confirmationManagement.activateAutoConfirmation(controllerId, null, null);
// do assignment and verify
assertThat(assignDistributionSet(dsId, controllerId).getAssignedEntity()).hasSize(1);
assertThat(confirmationManagement.findActiveActionsWaitingConfirmation(controllerId)).isEmpty();
assertThat(deploymentManagement.findActionsByTarget(controllerId, PAGE).getContent()).hasSize(1)
.allMatch(action -> action.getStatus() == Status.RUNNING);
}
@ParameterizedTest
@MethodSource("getAutoConfirmationArguments")
@Description("Verify activating auto confirmation with different parameters")
void verifyAutoConfirmationActivationValues(final String initiator, final String remark) {
final String controllerId = testdataFactory.createTarget().getControllerId();
confirmationManagement.activateAutoConfirmation(controllerId, initiator, remark);
assertThat(targetManagement.getByControllerID(controllerId)).hasValueSatisfying(target -> {
assertThat(target.getAutoConfirmationStatus()).isNotNull()
.matches(status -> status.getTarget().getControllerId().equals(controllerId))
.matches(status -> Objects.equals(status.getInitiator(), initiator))
.matches(status -> Objects.equals(status.getCreatedBy(), "bumlux"))
.matches(status -> Objects.equals(status.getRemark(), remark)).satisfies(status -> {
final Instant activationTime = Instant.ofEpochMilli(status.getActivatedAt());
assertThat(activationTime).isAfterOrEqualTo(activationTime.minusSeconds(3L));
});
});
confirmationManagement.deactivateAutoConfirmation(controllerId);
verifyAutoConfirmationIsDisabled(controllerId);
}
private static Stream<Arguments> getAutoConfirmationArguments() {
return Stream.of(Arguments.of("TestUser", "TestRemark"), Arguments.of("TestUser", null),
Arguments.of(null, "TestRemark"), Arguments.of(null, null));
}
@Test
@Description("Verify activating already active auto confirmation will throw exception.")
void verifyActivateAlreadyActiveAutoConfirmationThrowException() {
final String controllerId = testdataFactory.createTarget().getControllerId();
confirmationManagement.activateAutoConfirmation(controllerId, "any", "any");
assertThat(targetManagement.getByControllerID(controllerId))
.hasValueSatisfying(target -> assertThat(target.getAutoConfirmationStatus()).isNotNull());
assertThatThrownBy(() -> confirmationManagement.activateAutoConfirmation(controllerId, "any", "any"))
.isInstanceOf(AutoConfirmationAlreadyActiveException.class)
.hasMessage("Auto confirmation is already active for device " + controllerId);
}
@Test
@Description("Verify disabling already disabled auto confirmation will not have any affect.")
void disableAlreadyDisabledAutoConfirmationHaveNoAffect() {
final String controllerId = testdataFactory.createTarget().getControllerId();
verifyAutoConfirmationIsDisabled(controllerId);
confirmationManagement.deactivateAutoConfirmation(controllerId);
verifyAutoConfirmationIsDisabled(controllerId);
}
private void verifyAutoConfirmationIsDisabled(final String controllerId) {
assertThat(targetManagement.getByControllerID(controllerId))
.hasValueSatisfying(target -> assertThat(target.getAutoConfirmationStatus()).isNull());
}
private static DeploymentRequest toDeploymentRequest(final String controllerId, final Long distributionSetId) {
return new DeploymentRequestBuilder(controllerId, distributionSetId).setConfirmationRequired(true).build();
}
}

View File

@@ -796,6 +796,8 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
assertThat(targetManagement.getByControllerID(DEFAULT_CONTROLLER_ID).get().getUpdateStatus())
.isEqualTo(TargetUpdateStatus.IN_SYNC);
assertThat(actionRepository.findById(action.getId()))
.hasValueSatisfying(a -> assertThat(a.getStatus()).isEqualTo(Status.FINISHED));
assertThat(actionStatusRepository.count()).isEqualTo(3);
assertThat(controllerManagement.findActionStatusByAction(PAGE, action.getId()).getNumberOfElements())
.isEqualTo(3);
@@ -806,7 +808,8 @@ class ControllerManagementTest extends AbstractJpaIntegrationTest {
+ "configured to accept them.")
@ExpectEvents({ @Expect(type = TargetCreatedEvent.class, count = 1),
@Expect(type = DistributionSetCreatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1), @Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = ActionCreatedEvent.class, count = 1),
@Expect(type = ActionUpdatedEvent.class, count = 1),
@Expect(type = TargetUpdatedEvent.class, count = 2),
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
@Expect(type = TargetAttributesRequestedEvent.class, count = 1),

View File

@@ -10,6 +10,8 @@ package org.eclipse.hawkbit.repository.jpa;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.eclipse.hawkbit.repository.model.Action.Status.RUNNING;
import static org.eclipse.hawkbit.repository.model.Action.Status.WAIT_FOR_CONFIRMATION;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.AbstractMap.SimpleEntry;
@@ -19,8 +21,10 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.validation.ConstraintViolationException;
@@ -58,6 +62,7 @@ import org.eclipse.hawkbit.repository.model.Action.ActionType;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
import org.eclipse.hawkbit.repository.model.DeploymentRequestBuilder;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
@@ -70,6 +75,8 @@ import org.eclipse.hawkbit.repository.test.matcher.Expect;
import org.eclipse.hawkbit.repository.test.matcher.ExpectEvents;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@@ -284,7 +291,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
assignDistributionSet(cancelDs, targets).getAssignedEntity();
assertThat(deploymentManagement.countActionsAll()).isEqualTo(quotaManagement.getMaxTargetsPerAutoAssignment());
assignDistributionSet(cancelDs2, targets).getAssignedEntity();
assertThat(deploymentManagement.countActionsAll()).isEqualTo(2L * quotaManagement.getMaxTargetsPerAutoAssignment());
assertThat(deploymentManagement.countActionsAll())
.isEqualTo(2L * quotaManagement.getMaxTargetsPerAutoAssignment());
}
@Test
@@ -546,13 +554,13 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
final DistributionSet ds1 = testdataFactory.createDistributionSet("1");
assignDistributionSet(ds1, targets);
assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_ACTIVE, Status.RUNNING);
assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_ACTIVE, RUNNING);
// Second assignment
final DistributionSet ds2 = testdataFactory.createDistributionSet("2");
assignDistributionSet(ds2, targets);
assertDsExclusivelyAssignedToTargets(targets, ds2.getId(), STATE_ACTIVE, Status.RUNNING);
assertDsExclusivelyAssignedToTargets(targets, ds2.getId(), STATE_ACTIVE, RUNNING);
assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_INACTIVE, Status.CANCELED);
assertThat(targetManagement.findByAssignedDistributionSet(PAGE, ds2.getId()).getContent()).hasSize(10)
@@ -583,14 +591,14 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
final DistributionSet ds1 = testdataFactory.createDistributionSet("Multi-assign-1");
assignDistributionSet(ds1.getId(), targetIds, 77);
assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_ACTIVE, Status.RUNNING);
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, Status.RUNNING);
assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_ACTIVE, Status.RUNNING);
assertDsExclusivelyAssignedToTargets(targets, ds2.getId(), STATE_ACTIVE, RUNNING);
assertDsExclusivelyAssignedToTargets(targets, ds1.getId(), STATE_ACTIVE, RUNNING);
}
private void assertDsExclusivelyAssignedToTargets(final List<Target> targets, final long dsId, final boolean active,
@@ -651,7 +659,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
void cancelMultiAssignmentActions() {
final List<Target> targets = testdataFactory.createTargets(1);
final List<DistributionSet> distributionSets = testdataFactory.createDistributionSets(4);
final List<DeploymentRequest> deploymentRequests = createAssignmentRequests(distributionSets, targets, 34);
final List<DeploymentRequest> deploymentRequests = createAssignmentRequests(distributionSets, targets, 34,
false);
enableMultiAssignments();
final List<DistributionSetAssignmentResult> results = deploymentManagement
@@ -672,9 +681,15 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
protected List<DeploymentRequest> createAssignmentRequests(final Collection<DistributionSet> distributionSets,
final Collection<Target> targets, final int weight) {
return createAssignmentRequests(distributionSets, targets, weight, false);
}
protected 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(DeploymentManagement
.deploymentRequest(target.getControllerId(), ds.getId()).setWeight(weight).build())));
distributionSets.forEach(ds -> targets.forEach(target -> deploymentRequests
.add(DeploymentManagement.deploymentRequest(target.getControllerId(), ds.getId()).setWeight(weight)
.setConfirmationRequired(confirmationRequired).build())));
return deploymentRequests;
}
@@ -713,7 +728,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
knownTargetIds.add(notExistingId);
final List<DistributionSetAssignmentResult> assignDistributionSetsResults = assignDistributionSetToTargets(
createdDs, knownTargetIds);
createdDs, knownTargetIds, false);
for (final DistributionSetAssignmentResult assignDistributionSetsResult : assignDistributionSetsResults) {
assertThat(assignDistributionSetsResult.getAlreadyAssigned()).isZero();
@@ -722,12 +737,152 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
}
}
@ParameterizedTest
@ValueSource(booleans = { true, false })
@Description("Assignments with confirmation flow active will result in actions in 'WAIT_FOR_CONFIRMATION' state")
void assignmentWithConfirmationFlowActive(final boolean confirmationRequired) {
final List<String> controllerIds = testdataFactory.createTargets(1).stream().map(Target::getControllerId)
.collect(Collectors.toList());
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
enableConfirmationFlow();
List<DistributionSetAssignmentResult> results = assignDistributionSetToTargets(distributionSet, controllerIds,
confirmationRequired);
assertThat(getResultingActionCount(results)).isEqualTo(controllerIds.size());
controllerIds.forEach(controllerId -> {
actionRepository.findByTargetControllerId(PAGE, controllerId).forEach(action -> {
assertThat(action.getDistributionSet().getId()).isIn(distributionSet.getId());
assertThat(action.getInitiatedBy()).as("Should be Initiated by current user")
.isEqualTo(tenantAware.getCurrentUsername());
if (confirmationRequired) {
assertThat(action.getStatus()).isEqualTo(Status.WAIT_FOR_CONFIRMATION);
} else {
assertThat(action.getStatus()).isEqualTo(RUNNING);
}
});
});
}
@ParameterizedTest
@ValueSource(booleans = { true, false })
@Description("Verify auto confirmation assignments and check action status with messages")
void assignmentWithAutoConfirmationWillBeHandledCorrectly(final boolean confirmationRequired) {
enableConfirmationFlow();
final Target target = testdataFactory.createTarget();
assertThat(target.getAutoConfirmationStatus()).isNull();
confirmationManagement.activateAutoConfirmation(target.getControllerId(), "not_bumlux", "my personal remark");
assertThat(targetManagement.getByControllerID(target.getControllerId()))
.hasValueSatisfying(t -> assertThat(t.getAutoConfirmationStatus()).isNotNull());
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
assignDistributionSets(Collections
.singletonList(new DeploymentRequestBuilder(target.getControllerId(), distributionSet.getId())
.setConfirmationRequired(confirmationRequired).build()));
assertThat(deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent()).hasSize(1)
.allSatisfy(action -> {
assertThat(action.getStatus()).isEqualTo(RUNNING);
assertThat(actionStatusRepository.getByActionId(PAGE, action.getId())).hasSize(1)
.allSatisfy(status -> {
final JpaActionStatus actionStatus = (JpaActionStatus) status;
assertThat(actionStatus.getStatus()).isEqualTo(WAIT_FOR_CONFIRMATION);
if (confirmationRequired) {
// confirmation of assignment is basically required, but active
// auto-confirmation will perform the confirmation
assertThat(actionStatus.getMessages())
.contains("Assignment initiated by user 'bumlux'")
.contains("Assignment automatically confirmed by initiator 'not_bumlux'. \n"
+ "\n" + "Auto confirmation activated by system user: 'bumlux' \n"
+ "\n" + "Remark: my personal remark");
} else {
// assignment never required confirmation, auto-confirmation will not be
// applied.
// assignment initiator has confirmed the action already
assertThat(actionStatus.getMessages())
.contains("Assignment initiated by user 'bumlux'")
.contains("Assignment confirmed by initiator [bumlux].");
}
});
});
}
@Test
@Description("Multiple assignments with confirmation flow active will result in correct cancel behaviour")
void multipleAssignmentWithConfirmationFlowActiveVerifyCancelBehaviour() {
final Target target = testdataFactory.createTarget("firstDevice");
final DistributionSet firstDs = testdataFactory.createDistributionSet();
final DistributionSet secondDs = testdataFactory.createDistributionSet();
enableConfirmationFlow();
final List<Action> resultActions = assignDistributionSet(firstDs.getId(), target.getControllerId())
.getAssignedEntity();
assertThat(resultActions).hasSize(1);
assertThat(resultActions.get(0)).satisfies(action -> {
assertThat(action.getDistributionSet().getId()).isEqualTo(firstDs.getId());
assertThat(action.getStatus()).isEqualTo(Status.WAIT_FOR_CONFIRMATION);
});
final List<Action> resultActions2 = assignDistributionSet(secondDs.getId(), target.getControllerId())
.getAssignedEntity();
assertThat(resultActions2).hasSize(1);
assertThat(resultActions2.get(0)).satisfies(action -> {
assertThat(action.getDistributionSet().getId()).isEqualTo(secondDs.getId());
assertThat(action.getStatus()).isEqualTo(Status.WAIT_FOR_CONFIRMATION);
});
final List<Action> actions = actionRepository.findByTargetControllerId(PAGE, target.getControllerId())
.getContent();
assertThat(actions).hasSize(2)
.anyMatch(action -> Objects.equals(action.getDistributionSet().getId(), firstDs.getId())
&& action.getStatus() == Status.CANCELING)
.anyMatch(action -> Objects.equals(action.getDistributionSet().getId(), secondDs.getId())
&& action.getStatus() == Status.WAIT_FOR_CONFIRMATION);
}
@Test
@Description("Assignments with confirmation flow deactivated will result in actions in only in 'RUNNING' state")
void verifyConfirmationRequiredFlagHaveNoInfluenceIfFlowIsDeactivated() {
final List<String> targets1 = testdataFactory.createTargets("group1", 1).stream().map(Target::getControllerId)
.collect(Collectors.toList());
final List<String> targets2 = testdataFactory.createTargets("group2", 1).stream().map(Target::getControllerId)
.collect(Collectors.toList());
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
final List<DistributionSetAssignmentResult> results = Stream
.concat(assignDistributionSetToTargets(distributionSet, targets1, true).stream(), //
assignDistributionSetToTargets(distributionSet, targets2, false).stream()) //
.collect(Collectors.toList());
final List<String> controllerIds = Stream.concat(targets1.stream(), targets2.stream())
.collect(Collectors.toList());
assertThat(getResultingActionCount(results)).isEqualTo(controllerIds.size());
controllerIds.forEach(controllerId -> {
actionRepository.findByTargetControllerId(PAGE, controllerId).forEach(action -> {
assertThat(action.getDistributionSet().getId()).isIn(distributionSet.getId());
assertThat(action.getInitiatedBy()).as("Should be Initiated by current user")
.isEqualTo(tenantAware.getCurrentUsername());
assertThat(action.getStatus()).isEqualTo(RUNNING);
});
});
}
private List<DistributionSetAssignmentResult> assignDistributionSetToTargets(final DistributionSet distributionSet,
final Iterable<String> targetIds) {
final Iterable<String> targetIds, final boolean confirmationRequired) {
final List<DeploymentRequest> deploymentRequests = new ArrayList<>();
for (final String controllerId : targetIds) {
deploymentRequests.add(new DeploymentRequest(controllerId, distributionSet.getId(), ActionType.FORCED, 0,
null, null, null, null));
null, null, null, null, confirmationRequired));
}
return deploymentManagement.assignDistributionSets(deploymentRequests);
}

View File

@@ -22,6 +22,7 @@ import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
@@ -31,6 +32,7 @@ import org.assertj.core.api.Assertions;
import org.assertj.core.api.Condition;
import org.awaitility.Awaitility;
import org.awaitility.Duration;
import org.eclipse.hawkbit.repository.Identifiable;
import org.eclipse.hawkbit.repository.OffsetBasedPageRequest;
import org.eclipse.hawkbit.repository.builder.RolloutCreate;
import org.eclipse.hawkbit.repository.builder.RolloutGroupCreate;
@@ -81,6 +83,9 @@ import org.eclipse.hawkbit.repository.test.util.WithSpringAuthorityRule;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
@@ -139,6 +144,42 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
assertThat(rolloutCreatedAction.getStatus()).isEqualTo(Status.FINISHED);
}
@ParameterizedTest
@MethodSource("simpleRolloutsPossibilities")
@Description("Verifies that action states are correctly initialized after starting a rollout with different options in regard to the confirmation.")
void runRolloutWithConfirmationFlagAndCoonfirmationFlowOptions(final boolean confirmationFlowActive,
final boolean confirmationRequired, final Status expectedStatus) {
// manually assign distribution set to target
final String knownControllerId = "controller12345";
final DistributionSet knownDistributionSet = testdataFactory.createDistributionSet();
testdataFactory.createTarget(knownControllerId);
if (confirmationFlowActive) {
enableConfirmationFlow();
}
// create rollout with the same distribution set already assigned
// start rollout
final Rollout rollout = testdataFactory.createRolloutByVariables("rolloutNotCancelRunningAction", "description",
1, "name==*", knownDistributionSet, "50", "5", confirmationRequired);
rolloutManagement.start(rollout.getId());
rolloutManagement.handleRollouts();
// verify that manually created action is still running and action
// created from rollout is finished
final List<Action> actionsByKnownTarget = deploymentManagement.findActionsByTarget(knownControllerId, PAGE)
.getContent();
assertThat(actionsByKnownTarget).hasSize(1);
assertThat(actionsByKnownTarget.get(0).getStatus()).isEqualTo(expectedStatus);
}
private static Stream<Arguments> simpleRolloutsPossibilities() {
return Stream.of(Arguments.of(true, true, Status.WAIT_FOR_CONFIRMATION), //
Arguments.of(true, false, Status.RUNNING), //
Arguments.of(false, true, Status.RUNNING), //
Arguments.of(false, false, Status.RUNNING));//
}
@Test
@Description("Verifies that a running action is auto canceled by a rollout which assigns another distribution-set.")
void rolloutAssignsNewDistributionSetAndAutoCloseActiveActions() {
@@ -1369,7 +1410,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
.targetFilterQuery("controllerId==" + targetPrefixName + "-*").set(distributionSet);
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> rolloutManagement.create(rollout, amountGroups, conditions));
.isThrownBy(() -> rolloutManagement.create(rollout, amountGroups, false, conditions));
}
@Test
@@ -1540,6 +1581,117 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
}
@Test
@Description("Verify rollout execution with advanced group definition and confirmation flow active.")
void createRolloutWithGroupDefinitionAndConfirmationFlowActive() {
final String rolloutName = "rolloutTest4";
final int amountTargetsInGroup1 = 10;
final int amountTargetsInGroup2 = 20;
final RolloutGroupConditions conditions = new RolloutGroupConditionBuilder().withDefaults().build();
// Generate Targets for group 2
final RolloutCreate rolloutcreate = generateTargetsAndRollout(rolloutName, amountTargetsInGroup2);
// Generate Targets for group 1
testdataFactory.createTargets(amountTargetsInGroup1, rolloutName + "-gr1-", rolloutName);
final List<RolloutGroupCreate> rolloutGroups = new ArrayList<>(3);
rolloutGroups.add(generateRolloutGroup(0, 100, "id==" + rolloutName + "-gr1-*", true));
rolloutGroups.add(generateRolloutGroup(1, 100, null, false));
// enable confirmation flow
enableConfirmationFlow();
final Long rolloutId = rolloutManagement.create(rolloutcreate, rolloutGroups, conditions).getId();
assertThat(getRollout(rolloutId)).satisfies(rollout -> {
assertThat(rollout.getStatus()).isEqualTo(RolloutStatus.CREATING);
for (final RolloutGroup group : rolloutGroupManagement.findByRollout(PAGE, rollout.getId()).getContent()) {
assertThat(group.getStatus()).isEqualTo(RolloutGroupStatus.CREATING);
}
});
// first handle iteration will put rollout in ready state
rolloutManagement.handleRollouts();
assertThat(getRollout(rolloutId)).satisfies(rollout -> {
assertThat(rollout.getStatus()).isEqualTo(RolloutStatus.READY);
assertThat(rollout.getTotalTargets()).isEqualTo(amountTargetsInGroup1 + amountTargetsInGroup2);
});
// verify created rollout groups
final List<Long> rolloutGroupIds = rolloutGroupManagement.findByRollout(PAGE, rolloutId).getContent().stream()
.map(Identifiable::getId).collect(Collectors.toList());
assertThat(rolloutGroupIds).hasSize(2);
assertRolloutGroup(rolloutGroupIds.get(0), RolloutGroupStatus.READY, true, amountTargetsInGroup1, null);
assertRolloutGroup(rolloutGroupIds.get(1), RolloutGroupStatus.READY, false, amountTargetsInGroup2, null);
// start rollout
rolloutManagement.start(rolloutId);
rolloutManagement.handleRollouts();
// verify rollout started. Check groups are in right state.
// Group 1 should be in WFC state, since confirmation is required here.
assertRolloutGroup(rolloutGroupIds.get(0), RolloutGroupStatus.RUNNING, true, amountTargetsInGroup1,
Status.WAIT_FOR_CONFIRMATION);
assertRolloutGroup(rolloutGroupIds.get(1), RolloutGroupStatus.SCHEDULED, false, amountTargetsInGroup2,
Status.SCHEDULED);
// cancel execution of all action of group 1 to trigger second group
forceQuitAllActionsOfRolloutGroup(rolloutGroupIds.get(0));
rolloutManagement.handleRollouts();
assertRolloutGroup(rolloutGroupIds.get(0), RolloutGroupStatus.FINISHED, true, amountTargetsInGroup1,
Status.CANCELED);
assertRolloutGroup(rolloutGroupIds.get(1), RolloutGroupStatus.SCHEDULED, false, amountTargetsInGroup2,
Status.SCHEDULED);
// verify actions of second rule are directly in RUNNING state, since
// confirmation is not required for this group
rolloutManagement.handleRollouts();
assertRolloutGroup(rolloutGroupIds.get(0), RolloutGroupStatus.FINISHED, true, amountTargetsInGroup1,
Status.CANCELED);
assertRolloutGroup(rolloutGroupIds.get(1), RolloutGroupStatus.RUNNING, false, amountTargetsInGroup2,
Status.RUNNING);
}
private void assertRolloutGroup(final long rolloutGroupId, final RolloutGroupStatus status,
final boolean isConfirmationRequired, final long totalTargets, final Status actionStatusToCheck) {
assertThat(rolloutGroupManagement.get(rolloutGroupId)).hasValueSatisfying(rolloutGroup -> {
assertThat(rolloutGroup.getStatus()).isEqualTo(status);
assertThat(rolloutGroup.isConfirmationRequired()).isEqualTo(isConfirmationRequired);
assertThat(rolloutGroup.getTotalTargets()).isEqualTo(totalTargets);
if (actionStatusToCheck != null) {
assertAllActionOfRolloutGroupHavingStatus(rolloutGroup.getId(), actionStatusToCheck);
}
});
}
private void assertAllActionOfRolloutGroupHavingStatus(final long rolloutGroupId, final Status status) {
final List<Target> targets = rolloutGroupManagement.findTargetsOfRolloutGroup(PAGE, rolloutGroupId)
.getContent();
targets.forEach(target -> {
final List<Action> activeActions = deploymentManagement
.findActionsByTarget(target.getControllerId(), PAGE).getContent();
assertThat(activeActions).hasSize(1);
assertThat(activeActions.get(0).getStatus()).isEqualTo(status);
});
}
private void forceQuitAllActionsOfRolloutGroup(final long rolloutGroupId) {
final List<Target> targets = rolloutGroupManagement.findTargetsOfRolloutGroup(PAGE, rolloutGroupId)
.getContent();
targets.forEach(target -> {
deploymentManagement.findActiveActionsByTarget(PAGE, target.getControllerId()).getContent().stream().map(Identifiable::getId)
.forEach(actionId -> {
deploymentManagement.cancelAction(actionId);
deploymentManagement.forceQuitAction(actionId);
});
});
}
@Test
@Description("Verify rollout creation fails if group definition does not address all targets")
void createRolloutWithGroupsNotMatchingTargets() {
@@ -1593,7 +1745,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
final RolloutCreate rollout = generateTargetsAndRollout(rolloutName, targets);
assertThatExceptionOfType(AssignmentQuotaExceededException.class)
.isThrownBy(() -> rolloutManagement.create(rollout, maxGroups + 1, conditions))
.isThrownBy(() -> rolloutManagement.create(rollout, maxGroups + 1, false, conditions))
.withMessageContaining("not be greater than " + maxGroups);
}
@@ -1615,7 +1767,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
final RolloutCreate rolloutToCreate = entityFactory.rollout().create().name(rolloutName)
.description("some description").targetFilterQuery("id==" + rolloutName + "-*").set(distributionSet);
Rollout myRollout = rolloutManagement.create(rolloutToCreate, amountGroups, conditions);
Rollout myRollout = rolloutManagement.create(rolloutToCreate, amountGroups, false, conditions);
myRollout = getRollout(myRollout.getId());
assertThat(myRollout.getStatus()).isEqualTo(RolloutStatus.CREATING);
@@ -1787,7 +1939,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
.name(prefixRolloutRunning + "-testRollout").targetFilterQuery("name==" + randomString + "*")
.set(testDs);
Rollout rolloutRunning = rolloutManagement.create(rolloutRunningCreate, 1, conditions);
Rollout rolloutRunning = rolloutManagement.create(rolloutRunningCreate, 1, false, conditions);
// Let the executor handle created Rollout
rolloutManagement.handleRollouts();
// start the rollout, so it has active running actions and a group which
@@ -1799,7 +1951,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
final String prefixRolloutReady = randomString + "2";
final RolloutCreate rolloutReadyCreate = entityFactory.rollout().create()
.name(prefixRolloutReady + "-testRollout").targetFilterQuery("name==" + randomString + "*").set(testDs);
Rollout rolloutReady = rolloutManagement.create(rolloutReadyCreate, 1, conditions);
Rollout rolloutReady = rolloutManagement.create(rolloutReadyCreate, 1, false, conditions);
// Let the executor handle created Rollout
rolloutManagement.handleRollouts();
rolloutReady = reloadRollout(rolloutReady);
@@ -1959,7 +2111,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
final RolloutCreate rolloutToCreate = entityFactory.rollout().create().name(rolloutName)
.targetFilterQuery("name==*").set(testDs);
final Rollout createdRollout = rolloutManagement.create(rolloutToCreate, 1, conditions);
final Rollout createdRollout = rolloutManagement.create(rolloutToCreate, 1, false, conditions);
// Let the executor handle created Rollout
rolloutManagement.handleRollouts();
@@ -1982,8 +2134,14 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
private RolloutGroupCreate generateRolloutGroup(final int index, final Integer percentage,
final String targetFilter) {
return generateRolloutGroup(index, percentage, targetFilter, false);
}
private RolloutGroupCreate generateRolloutGroup(final int index, final Integer percentage,
final String targetFilter, final boolean confirmationRequired) {
return entityFactory.rolloutGroup().create().name("Group" + index).description("Group" + index + "desc")
.targetPercentage(Float.valueOf(percentage)).targetFilterQuery(targetFilter);
.targetPercentage(Float.valueOf(percentage)).targetFilterQuery(targetFilter)
.confirmationRequired(confirmationRequired);
}
private RolloutCreate generateTargetsAndRollout(final String rolloutName, final int amountTargetsForRollout) {
@@ -2033,7 +2191,7 @@ class RolloutManagementTest extends AbstractJpaIntegrationTest {
testdataFactory.createTargets(amountTargetsForRollout, targetPrefixName + "-", targetPrefixName);
return testdataFactory.createRolloutByVariables(rolloutName, rolloutName + "description", groupSize,
"controllerId==" + targetPrefixName + "-*", dsForRolloutTwo, successCondition, errorCondition,
Action.ActionType.FORCED, weight);
Action.ActionType.FORCED, weight, false);
}
private int changeStatusForAllRunningActions(final Rollout rollout, final Status status) {

View File

@@ -33,6 +33,9 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetType;
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
@@ -174,6 +177,69 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest {
verifyThatTargetsNotHaveDistributionSetAssignment(toAssignDs, targets.subList(1, 25));
}
@ParameterizedTest
@MethodSource("confirmationOptions")
@Description("Test auto assignment of a DS to filtered targets with different confirmation options")
void checkAutoAssignWithConfirmationOptions(final boolean confirmationFlowActive, final boolean confirmationRequired,
final Action.Status expectedStatus) {
final DistributionSet distributionSet = testdataFactory.createDistributionSet("dsA");
if (confirmationFlowActive) {
enableConfirmationFlow();
}
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement.updateAutoAssignDS(entityFactory
.targetFilterQuery()
.updateAutoAssign(targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*")).getId())
.ds(distributionSet.getId()).confirmationRequired(confirmationRequired));
final String targetDsAIdPref = "targ";
final List<Target> targets = testdataFactory.createTargets(20, targetDsAIdPref,
targetDsAIdPref.concat(" description"));
// Run the check
autoAssignChecker.checkAllTargets();
verifyThatTargetsHaveDistributionSetAssignedAndActionStatus(distributionSet, targets, expectedStatus);
}
@ParameterizedTest
@MethodSource("confirmationOptions")
@Description("Test auto assignment of a DS for a specific device with different confirmation options")
void checkAutoAssignmentForDeviceWithConfirmationRequired(final boolean confirmationFlowActive,
final boolean confirmationRequired, final Action.Status expectedStatus) {
final DistributionSet toAssignDs = testdataFactory.createDistributionSet();
if (confirmationFlowActive) {
enableConfirmationFlow();
}
// target filter query that matches all targets
targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
.updateAutoAssign(targetFilterQueryManagement
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*")).getId())
.ds(toAssignDs.getId()).confirmationRequired(confirmationRequired));
final List<Target> targets = testdataFactory.createTargets(25);
// Run the check
autoAssignChecker.checkSingleTarget(targets.get(0).getControllerId());
verifyThatTargetsHaveDistributionSetAssignedAndActionStatus(toAssignDs, targets.subList(0, 1), expectedStatus);
verifyThatTargetsNotHaveDistributionSetAssignment(toAssignDs, targets.subList(1, 25));
}
private static Stream<Arguments> confirmationOptions() {
return Stream.of( //
Arguments.of(true, true, Status.WAIT_FOR_CONFIRMATION), //
Arguments.of(true, false, Status.RUNNING), //
Arguments.of(false, true, Status.RUNNING), //
Arguments.of(false, false, Status.RUNNING));
}
@Test
@Description("Test auto assignment of an incomplete DS to filtered targets, that causes failures")
void checkAutoAssignWithFailures() {
@@ -246,6 +312,22 @@ class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest {
}
@Step
private void verifyThatTargetsHaveDistributionSetAssignedAndActionStatus(final DistributionSet set,
final List<Target> targets, final Action.Status status) {
final List<String> targetIds = targets.stream().map(Target::getControllerId).collect(Collectors.toList());
final List<Target> targetsWithAssignedDS = targetManagement.findByAssignedDistributionSet(PAGE, set.getId())
.getContent();
assertThat(targetsWithAssignedDS).isNotEmpty();
assertThat(targetsWithAssignedDS).allMatch(target -> targetIds.contains(target.getControllerId()));
final List<Action> actionsByDs = deploymentManagement.findActionsByDistributionSet(PAGE, set.getId())
.getContent();
assertThat(actionsByDs).hasSize(targets.size());
assertThat(actionsByDs).allMatch(action -> action.getStatus() == status);
}
@Step
private void verifyThatTargetsNotHaveDistributionSetAssignment(final DistributionSet set,
final List<Target> targets) {

View File

@@ -99,7 +99,7 @@ public class RSQLRolloutGroupFieldTest extends AbstractJpaIntegrationTest {
return rolloutManagement.create(
entityFactory.rollout().create().set(distributionSetManagement.get(distributionSetId).get()).name(name)
.targetFilterQuery(targetFilterQuery),
amountGroups, new RolloutGroupConditionBuilder().withDefaults()
amountGroups, false, new RolloutGroupConditionBuilder().withDefaults()
.successCondition(RolloutGroupSuccessCondition.THRESHOLD, "100").build());
}
}