Provide API for auto assignment check for specific device (#1262)
* Extend the auto assign executor to execute a auto assignment check for a specific device. Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io> * add tests and handle exceptions Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io> * add unit test Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io> * add Copyright header Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io> * fix Copyright header Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io> * add authorization Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io> * fix exception handling and authorization Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io> * renaming Signed-off-by: Stefan Klotz <stefan.klotz@bosch.io> * Rename auto assignment functions Signed-off-by: Michael Herdt <Michael.Herdt@bosch.io> Co-authored-by: Stefan Klotz <stefan.klotz@bosch.io>
This commit is contained in:
@@ -16,6 +16,7 @@ import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -53,6 +54,7 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.TargetMetadataCompositeKey;
|
||||
import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
|
||||
import org.eclipse.hawkbit.repository.jpa.specifications.TargetSpecifications;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
@@ -853,6 +855,25 @@ public class JpaTargetManagement implements TargetManagement {
|
||||
return targetRepository.exists(TargetSpecifications.hasControllerId(controllerId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTargetMatchingQueryAndDSNotAssignedAndCompatible(final String controllerId,
|
||||
final long distributionSetId, final String targetFilterQuery) {
|
||||
RSQLUtility.validateRsqlFor(targetFilterQuery, TargetFields.class);
|
||||
final DistributionSet ds = distributionSetManagement.get(distributionSetId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, distributionSetId));
|
||||
final Long distSetTypeId = ds.getType().getId();
|
||||
final List<Specification<JpaTarget>> specList = Arrays.asList(
|
||||
RSQLUtility.buildRsqlSpecification(targetFilterQuery, TargetFields.class, virtualPropertyReplacer,
|
||||
database),
|
||||
TargetSpecifications.hasNotDistributionSetInActions(distributionSetId),
|
||||
TargetSpecifications.isCompatibleWithDistributionSetType(distSetTypeId),
|
||||
TargetSpecifications.hasControllerId(controllerId));
|
||||
|
||||
final Specification<JpaTarget> combinedSpecification = Objects
|
||||
.requireNonNull(SpecificationsBuilder.combineWithAnd(specList));
|
||||
return targetRepository.exists(combinedSpecification);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<Target> findByControllerAttributesRequested(final Pageable pageReq) {
|
||||
return JpaManagementHelper.findAllWithCountBySpec(targetRepository, pageReq,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.autoassign;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -64,12 +65,21 @@ public class AutoAssignChecker extends AbstractAutoAssignExecutor {
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void check() {
|
||||
public void checkAllTargets() {
|
||||
LOGGER.debug("Auto assign check call for tenant {} started", getTenantAware().getCurrentTenant());
|
||||
forEachFilterWithAutoAssignDS(this::checkByTargetFilterQueryAndAssignDS);
|
||||
LOGGER.debug("Auto assign check call for tenant {} finished", getTenantAware().getCurrentTenant());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkSingleTarget(String controllerId) {
|
||||
LOGGER.debug("Auto assign check call for tenant {} and device {} started", getTenantAware().getCurrentTenant(),
|
||||
controllerId);
|
||||
forEachFilterWithAutoAssignDS(filter -> checkForDevice(controllerId, filter));
|
||||
LOGGER.debug("Auto assign check call for tenant {} and device {} finished", getTenantAware().getCurrentTenant(),
|
||||
controllerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the distribution set, gets all controllerIds and assigns the DS
|
||||
* to them. Catches PersistenceException and own exceptions derived from
|
||||
@@ -104,4 +114,23 @@ public class AutoAssignChecker extends AbstractAutoAssignExecutor {
|
||||
LOGGER.debug("Auto assign check call for tenant {} and target filter query id {} finished",
|
||||
getTenantAware().getCurrentTenant(), targetFilterQuery.getId());
|
||||
}
|
||||
|
||||
private void checkForDevice(final String controllerId, final TargetFilterQuery targetFilterQuery) {
|
||||
LOGGER.debug("Auto assign check call for tenant {} and target filter query id {} for device {} started",
|
||||
getTenantAware().getCurrentTenant(), targetFilterQuery.getId(), controllerId);
|
||||
try {
|
||||
final boolean controllerIdMatches = targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(
|
||||
controllerId, targetFilterQuery.getAutoAssignDistributionSet().getId(),
|
||||
targetFilterQuery.getQuery());
|
||||
|
||||
if (controllerIdMatches) {
|
||||
runTransactionalAssignment(targetFilterQuery, Collections.singletonList(controllerId));
|
||||
}
|
||||
|
||||
} catch (final PersistenceException | AbstractServerRtException e) {
|
||||
LOGGER.error("Error during auto assign check of target filter query id {}", targetFilterQuery.getId(), e);
|
||||
}
|
||||
LOGGER.debug("Auto assign check call for tenant {} and target filter query id {} finished",
|
||||
getTenantAware().getCurrentTenant(), targetFilterQuery.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ public class AutoAssignScheduler {
|
||||
|
||||
try {
|
||||
LOGGER.debug("Auto assign scheduled execution has aquired lock and started for each tenant.");
|
||||
systemManagement.forEachTenant(tenant -> autoAssignExecutor.check());
|
||||
systemManagement.forEachTenant(tenant -> autoAssignExecutor.checkAllTargets());
|
||||
} finally {
|
||||
lock.unlock();
|
||||
LOGGER.debug("Auto assign scheduled execution has released lock and finished.");
|
||||
|
||||
@@ -48,6 +48,8 @@ import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidTargetAddressException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetMetadata;
|
||||
@@ -1256,6 +1258,83 @@ class TargetManagementTest extends AbstractJpaIntegrationTest {
|
||||
validateFoundTargetsByRsql(rsqlOrControllerIdNotEqualFilter, controllerId1, controllerId2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Target matches filter.")
|
||||
void matchesFilter() {
|
||||
final Target target = createTargetWithMetadata("target1", 2);
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||
final String filter = "metadata.key1==target1-value1";
|
||||
|
||||
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target.getControllerId(),
|
||||
ds.getId(), filter)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Target does not matches filter.")
|
||||
void matchesFilterWrongFilter() {
|
||||
final Target target = testdataFactory.createTarget();
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||
final String filter = "metadata.key==not_existing";
|
||||
|
||||
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target.getControllerId(),
|
||||
ds.getId(), filter)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Target matches filter but DS already assigned.")
|
||||
void matchesFilterDsAssigned() {
|
||||
final Target target = testdataFactory.createTarget();
|
||||
final DistributionSet ds1 = testdataFactory.createDistributionSet();
|
||||
final DistributionSet ds2 = testdataFactory.createDistributionSet();
|
||||
assignDistributionSet(ds1, target);
|
||||
assignDistributionSet(ds2, target);
|
||||
final String filter = "name==*";
|
||||
|
||||
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target.getControllerId(),
|
||||
ds1.getId(), filter)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Target matches filter for DS with wrong type.")
|
||||
void matchesFilterWrongType() {
|
||||
final TargetType type = testdataFactory.createTargetType("type", Collections.emptyList());
|
||||
final Target target = testdataFactory.createTarget("target", "target", type.getId());
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||
|
||||
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target.getControllerId(),
|
||||
ds.getId(), "name==*")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Target matches filter that is invalid.")
|
||||
void matchesFilterInvalidFilter() {
|
||||
final String target = testdataFactory.createTarget().getControllerId();
|
||||
final Long ds = testdataFactory.createDistributionSet().getId();
|
||||
|
||||
assertThatExceptionOfType(RSQLParameterSyntaxException.class).isThrownBy(() -> targetManagement
|
||||
.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target, ds, "invalid_syntax"));
|
||||
assertThatExceptionOfType(RSQLParameterUnsupportedFieldException.class).isThrownBy(() -> targetManagement
|
||||
.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target, ds, "invalid_field==1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Target matches filter for not existing target.")
|
||||
void matchesFilterTargetNotExists() {
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||
|
||||
assertThat(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible("notExisting", ds.getId(),
|
||||
"name==*")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Target matches filter for not existing DS.")
|
||||
void matchesFilterDsNotExists() {
|
||||
final String target = testdataFactory.createTarget().getControllerId();
|
||||
|
||||
assertThatExceptionOfType(EntityNotFoundException.class).isThrownBy(
|
||||
() -> targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target, 123, "name==*"));
|
||||
}
|
||||
|
||||
private void validateFoundTargetsByRsql(final String rsqlFilter, final String... controllerIds) {
|
||||
final Slice<Target> foundTargetsByMetadataAndControllerId = targetManagement.findByRsql(PAGE, rsqlFilter);
|
||||
final long foundTargetsByMetadataAndControllerIdCount = targetManagement.countByRsql(rsqlFilter);
|
||||
|
||||
@@ -0,0 +1,411 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.autoassign;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.jpa.ActionRepository;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
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.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Step;
|
||||
import io.qameta.allure.Story;
|
||||
|
||||
/**
|
||||
* Test class for {@link AutoAssignChecker}.
|
||||
*
|
||||
*/
|
||||
@Feature("Component Tests - Repository")
|
||||
@Story("Auto assign checker")
|
||||
class AutoAssignCheckerIntTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
@Autowired
|
||||
private AutoAssignChecker autoAssignChecker;
|
||||
|
||||
@Autowired
|
||||
private ActionRepository actionRepository;
|
||||
|
||||
@Test
|
||||
@Description("Verifies that a running action is auto canceled by a AutoAssignment which assigns another distribution-set.")
|
||||
void autoAssignDistributionSetAndAutoCloseOldActions() {
|
||||
|
||||
tenantConfigurationManagement
|
||||
.addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, true);
|
||||
|
||||
try {
|
||||
final String knownControllerId = "controller12345";
|
||||
final DistributionSet firstDistributionSet = testdataFactory.createDistributionSet();
|
||||
final DistributionSet secondDistributionSet = testdataFactory.createDistributionSet("second");
|
||||
testdataFactory.createTarget(knownControllerId);
|
||||
final DistributionSetAssignmentResult assignmentResult = assignDistributionSet(firstDistributionSet.getId(),
|
||||
knownControllerId);
|
||||
final Long manuallyAssignedActionId = getFirstAssignedActionId(assignmentResult);
|
||||
|
||||
// target filter query that matches all targets
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*"));
|
||||
targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
|
||||
.updateAutoAssign(targetFilterQuery.getId()).ds(secondDistributionSet.getId()));
|
||||
// Run the check
|
||||
autoAssignChecker.checkAllTargets();
|
||||
|
||||
// verify that manually created action is canceled and action
|
||||
// created from AutoAssign is running
|
||||
final List<Action> actionsByKnownTarget = deploymentManagement.findActionsByTarget(knownControllerId, PAGE)
|
||||
.getContent();
|
||||
// should be 2 actions, one manually and one from the AutoAssign
|
||||
assertThat(actionsByKnownTarget).hasSize(2);
|
||||
// verify that manually assigned action is still running
|
||||
assertThat(deploymentManagement.findAction(manuallyAssignedActionId).get().getStatus())
|
||||
.isEqualTo(Status.CANCELED);
|
||||
// verify that AutoAssign created action is running
|
||||
final Action rolloutCreatedAction = actionsByKnownTarget.stream()
|
||||
.filter(action -> !action.getId().equals(manuallyAssignedActionId)).findAny().get();
|
||||
assertThat(rolloutCreatedAction.getStatus()).isEqualTo(Status.RUNNING);
|
||||
assertThat(rolloutCreatedAction.getActionType()).isEqualTo(ActionType.FORCED);
|
||||
} finally {
|
||||
tenantConfigurationManagement
|
||||
.addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test auto assignment of a DS to filtered targets")
|
||||
void checkAutoAssign() {
|
||||
|
||||
final DistributionSet setA = testdataFactory.createDistributionSet("dsA"); // will
|
||||
// be
|
||||
// auto
|
||||
// assigned
|
||||
final DistributionSet setB = testdataFactory.createDistributionSet("dsB");
|
||||
|
||||
// target filter query that matches all targets
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement.updateAutoAssignDS(entityFactory
|
||||
.targetFilterQuery()
|
||||
.updateAutoAssign(targetFilterQueryManagement
|
||||
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*")).getId())
|
||||
.ds(setA.getId()));
|
||||
|
||||
final String targetDsAIdPref = "targ";
|
||||
final List<Target> targets = testdataFactory.createTargets(25, targetDsAIdPref,
|
||||
targetDsAIdPref.concat(" description"));
|
||||
final int targetsCount = targets.size();
|
||||
|
||||
// assign set A to first 10 targets
|
||||
assignDistributionSet(setA, targets.subList(0, 10));
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setA, targets.subList(0, 10), targetsCount);
|
||||
|
||||
// assign set B to first 5 targets
|
||||
// they have now 2 DS in their action history and should not get updated
|
||||
// with dsA
|
||||
assignDistributionSet(setB, targets.subList(0, 5));
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount);
|
||||
|
||||
// assign set B to next 10 targets
|
||||
assignDistributionSet(setB, targets.subList(10, 20));
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(10, 20), targetsCount);
|
||||
|
||||
// Count the number of targets that will be assigned with setA
|
||||
assertThat(targetManagement.countByRsqlAndNonDSAndCompatible(setA.getId(), targetFilterQuery.getQuery()))
|
||||
.isEqualTo(15);
|
||||
|
||||
// Run the check
|
||||
autoAssignChecker.checkAllTargets();
|
||||
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setA, targets.subList(5, 25), targetsCount);
|
||||
|
||||
// first 5 should keep their dsB, because they already had the dsA once
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount);
|
||||
|
||||
verifyThatCreatedActionsAreInitiatedByCurrentUser(targetFilterQuery, setA, targets);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test auto assignment of a DS for a specific device")
|
||||
void checkAutoAssignmentForDevice() {
|
||||
|
||||
final DistributionSet toAssignDs = testdataFactory.createDistributionSet();
|
||||
|
||||
// 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()));
|
||||
|
||||
final List<Target> targets = testdataFactory.createTargets(25);
|
||||
final int targetsCount = targets.size();
|
||||
|
||||
// Run the check
|
||||
autoAssignChecker.checkSingleTarget(targets.get(0).getControllerId());
|
||||
|
||||
verifyThatTargetsHaveDistributionSetAssignment(toAssignDs, targets.subList(0, 1), targetsCount);
|
||||
|
||||
verifyThatTargetsNotHaveDistributionSetAssignment(toAssignDs, targets.subList(1, 25));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test auto assignment of an incomplete DS to filtered targets, that causes failures")
|
||||
void checkAutoAssignWithFailures() {
|
||||
|
||||
// incomplete distribution set that will be assigned
|
||||
final DistributionSet setF = distributionSetManagement.create(entityFactory.distributionSet().create()
|
||||
.name("dsA").version("1").type(testdataFactory.findOrCreateDefaultTestDsType()));
|
||||
final DistributionSet setA = testdataFactory.createDistributionSet("dsA");
|
||||
final DistributionSet setB = testdataFactory.createDistributionSet("dsB");
|
||||
|
||||
final String targetDsAIdPref = "targA";
|
||||
final String targetDsFIdPref = "targB";
|
||||
|
||||
// target filter query that matches first bunch of targets, that should
|
||||
// fail
|
||||
assertThatExceptionOfType(IncompleteDistributionSetException.class).isThrownBy(() -> {
|
||||
final Long filterId = targetFilterQueryManagement.create(
|
||||
entityFactory.targetFilterQuery().create().name("filterA").query("id==" + targetDsFIdPref + "*"))
|
||||
.getId();
|
||||
targetFilterQueryManagement
|
||||
.updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(setF.getId()));
|
||||
});
|
||||
// target filter query that matches failed bunch of targets
|
||||
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("filterB")
|
||||
.query("id==" + targetDsAIdPref + "*").autoAssignDistributionSet(setA.getId()));
|
||||
|
||||
final List<Target> targetsF = testdataFactory.createTargets(10, targetDsFIdPref,
|
||||
targetDsFIdPref.concat(" description"));
|
||||
|
||||
final List<Target> targetsA = testdataFactory.createTargets(10, targetDsAIdPref,
|
||||
targetDsAIdPref.concat(" description"));
|
||||
|
||||
final int targetsCount = targetsA.size() + targetsF.size();
|
||||
|
||||
// assign set B to first 5 targets of fail group
|
||||
assignDistributionSet(setB, targetsF.subList(0, 5));
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setB, targetsF.subList(0, 5), targetsCount);
|
||||
|
||||
// Run the check
|
||||
autoAssignChecker.checkAllTargets();
|
||||
|
||||
// first 5 targets of the fail group should still have setB
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setB, targetsF.subList(0, 5), targetsCount);
|
||||
|
||||
// all targets of A group should have received setA
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setA, targetsA, targetsCount);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param set
|
||||
* the expected distribution set
|
||||
* @param targets
|
||||
* the targets that should have it
|
||||
*/
|
||||
@Step
|
||||
private void verifyThatTargetsHaveDistributionSetAssignment(final DistributionSet set, final List<Target> targets,
|
||||
final int count) {
|
||||
final List<Long> targetIds = targets.stream().map(Target::getId).collect(Collectors.toList());
|
||||
|
||||
final Slice<Target> targetsAll = targetManagement.findAll(PAGE);
|
||||
assertThat(targetsAll).as("Count of targets").hasSize(count);
|
||||
|
||||
for (final Target target : targetsAll) {
|
||||
if (targetIds.contains(target.getId())) {
|
||||
assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).get())
|
||||
.as("assigned DS").isEqualTo(set);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Step
|
||||
private void verifyThatTargetsNotHaveDistributionSetAssignment(final DistributionSet set,
|
||||
final List<Target> targets) {
|
||||
final List<Long> targetIds = targets.stream().map(Target::getId).collect(Collectors.toList());
|
||||
|
||||
final Slice<Target> targetsAll = targetManagement.findAll(PAGE);
|
||||
|
||||
for (final Target target : targetsAll) {
|
||||
if (targetIds.contains(target.getId())) {
|
||||
assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId())).isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Step
|
||||
private void verifyThatCreatedActionsAreInitiatedByCurrentUser(final TargetFilterQuery targetFilterQuery,
|
||||
final DistributionSet distributionSet, final List<Target> targets) {
|
||||
final Set<String> targetIds = targets.stream().map(Target::getControllerId).collect(Collectors.toSet());
|
||||
|
||||
actionRepository.findByDistributionSetId(Pageable.unpaged(), distributionSet.getId()).stream()
|
||||
.filter(a -> targetIds.contains(a.getTarget().getControllerId()))
|
||||
.forEach(a -> assertThat(a.getInitiatedBy())
|
||||
.as("Action should be initiated by the user who initiated the auto assignment")
|
||||
.isEqualTo(targetFilterQuery.getAutoAssignInitiatedBy()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test auto assignment of a distribution set with FORCED, SOFT and DOWNLOAD_ONLY action types")
|
||||
void checkAutoAssignWithDifferentActionTypes() {
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
final String targetDsAIdPref = "A";
|
||||
final String targetDsBIdPref = "B";
|
||||
final String targetDsCIdPref = "C";
|
||||
|
||||
final List<Target> targetsA = createTargetsAndAutoAssignDistSet(targetDsAIdPref, 5, distributionSet,
|
||||
ActionType.FORCED);
|
||||
final List<Target> targetsB = createTargetsAndAutoAssignDistSet(targetDsBIdPref, 10, distributionSet,
|
||||
ActionType.SOFT);
|
||||
final List<Target> targetsC = createTargetsAndAutoAssignDistSet(targetDsCIdPref, 10, distributionSet,
|
||||
ActionType.DOWNLOAD_ONLY);
|
||||
|
||||
final int targetsCount = targetsA.size() + targetsB.size() + targetsC.size();
|
||||
|
||||
autoAssignChecker.checkAllTargets();
|
||||
|
||||
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsA, targetsCount);
|
||||
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsB, targetsCount);
|
||||
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsC, targetsCount);
|
||||
|
||||
verifyThatTargetsHaveAssignmentActionType(ActionType.FORCED, targetsA);
|
||||
verifyThatTargetsHaveAssignmentActionType(ActionType.SOFT, targetsB);
|
||||
verifyThatTargetsHaveAssignmentActionType(ActionType.DOWNLOAD_ONLY, targetsC);
|
||||
}
|
||||
|
||||
@Step
|
||||
private List<Target> createTargetsAndAutoAssignDistSet(final String prefix, final int targetCount,
|
||||
final DistributionSet distributionSet, final ActionType actionType) {
|
||||
|
||||
final List<Target> targets = testdataFactory.createTargets(targetCount, "target" + prefix,
|
||||
prefix.concat(" description"));
|
||||
targetFilterQueryManagement.create(
|
||||
entityFactory.targetFilterQuery().create().name("filter" + prefix).query("id==target" + prefix + "*")
|
||||
.autoAssignDistributionSet(distributionSet).autoAssignActionType(actionType));
|
||||
|
||||
return targets;
|
||||
}
|
||||
|
||||
@Step
|
||||
private void verifyThatTargetsHaveAssignmentActionType(final ActionType actionType, final List<Target> targets) {
|
||||
final List<Action> actions = targets.stream().map(Target::getControllerId).flatMap(
|
||||
controllerId -> deploymentManagement.findActionsByTarget(controllerId, PAGE).getContent().stream())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertThat(actions).hasSize(targets.size());
|
||||
assertThat(actions).allMatch(action -> action.getActionType().equals(actionType));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("An auto assignment target filter with weight creates actions with weights")
|
||||
void actionsWithWeightAreCreated() throws Exception {
|
||||
final int amountOfTargets = 5;
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||
final int weight = 32;
|
||||
enableMultiAssignments();
|
||||
|
||||
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("a").query("name==*")
|
||||
.autoAssignDistributionSet(ds).autoAssignWeight(weight));
|
||||
testdataFactory.createTargets(amountOfTargets);
|
||||
autoAssignChecker.checkAllTargets();
|
||||
|
||||
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
|
||||
assertThat(actions).hasSize(amountOfTargets);
|
||||
assertThat(actions).allMatch(action -> action.getWeight().get() == weight);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("An auto assignment target filter without weight still works after multi assignment is enabled")
|
||||
void filterWithoutWeightWorksInMultiAssignmentMode() throws Exception {
|
||||
final int amountOfTargets = 5;
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||
targetFilterQueryManagement.create(
|
||||
entityFactory.targetFilterQuery().create().name("a").query("name==*").autoAssignDistributionSet(ds));
|
||||
enableMultiAssignments();
|
||||
|
||||
testdataFactory.createTargets(amountOfTargets);
|
||||
autoAssignChecker.checkAllTargets();
|
||||
|
||||
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
|
||||
assertThat(actions).hasSize(amountOfTargets);
|
||||
assertThat(actions).allMatch(action -> !action.getWeight().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies an auto assignment only creates actions for compatible targets")
|
||||
void checkAutoAssignmentWithIncompatibleTargets() {
|
||||
final int TARGET_COUNT = 5;
|
||||
|
||||
final DistributionSet testDs = testdataFactory.createDistributionSet();
|
||||
final DistributionSetType incompatibleDsType1 = testdataFactory
|
||||
.findOrCreateDistributionSetType("incompatibleDsType1", "incompDsType1");
|
||||
final DistributionSetType incompatibleDsType2 = testdataFactory
|
||||
.findOrCreateDistributionSetType("incompatibleDsType2", "incompDsType2");
|
||||
final TargetFilterQuery testFilter = targetFilterQueryManagement.create(entityFactory.targetFilterQuery()
|
||||
.create().name("test-filter").query("name==*").autoAssignDistributionSet(testDs));
|
||||
|
||||
final TargetType incompatibleEmptyType = testdataFactory.createTargetType("incompatibleEmptyType",
|
||||
Collections.emptyList());
|
||||
final TargetType incompatibleSingleType = testdataFactory.createTargetType("incompatibleSingleType",
|
||||
Collections.singletonList(incompatibleDsType1));
|
||||
final TargetType incompatibleMultiType = testdataFactory.createTargetType("incompatibleMultiType",
|
||||
Arrays.asList(incompatibleDsType1, incompatibleDsType2));
|
||||
final TargetType compatibleSingleType = testdataFactory.createTargetType("compatibleSingleType",
|
||||
Collections.singletonList(testDs.getType()));
|
||||
final TargetType compatibleMultiType = testdataFactory.createTargetType("compatibleMultiType",
|
||||
Arrays.asList(testDs.getType(), incompatibleDsType1));
|
||||
|
||||
testdataFactory.createTargetsWithType(TARGET_COUNT, "incompatibleEmpty", incompatibleEmptyType);
|
||||
testdataFactory.createTargetsWithType(TARGET_COUNT, "incompatibleSingle", incompatibleSingleType);
|
||||
testdataFactory.createTargetsWithType(TARGET_COUNT, "incompatibleMulti", incompatibleMultiType);
|
||||
|
||||
final List<Target> compatibleTargetsSingleType = testdataFactory.createTargetsWithType(TARGET_COUNT,
|
||||
"compatibleSingle", compatibleSingleType);
|
||||
final List<Target> compatibleTargetsMultiType = testdataFactory.createTargetsWithType(TARGET_COUNT,
|
||||
"compatibleMulti", compatibleMultiType);
|
||||
final List<Target> compatibleTargetsWithoutType = testdataFactory.createTargets(TARGET_COUNT,
|
||||
"compatibleSingleWithoutType");
|
||||
|
||||
final List<Long> compatibleTargets = Stream
|
||||
.of(compatibleTargetsSingleType, compatibleTargetsMultiType, compatibleTargetsWithoutType)
|
||||
.flatMap(Collection::stream).map(Target::getId).collect(Collectors.toList());
|
||||
final long compatibleCount = targetManagement.countByRsqlAndNonDSAndCompatible(testDs.getId(),
|
||||
testFilter.getQuery());
|
||||
assertThat(compatibleCount).isEqualTo(compatibleTargets.size());
|
||||
|
||||
autoAssignChecker.checkAllTargets();
|
||||
|
||||
final List<Action> actions = deploymentManagement.findActionsAll(Pageable.unpaged()).getContent();
|
||||
assertThat(actions).hasSize(compatibleTargets.size());
|
||||
final List<Long> actionTargets = actions.stream().map(a -> a.getTarget().getId()).collect(Collectors.toList());
|
||||
assertThat(actionTargets).containsExactlyInAnyOrderElementsOf(compatibleTargets);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others.
|
||||
* 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
|
||||
@@ -8,366 +8,113 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.autoassign;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.jpa.ActionRepository;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
import org.eclipse.hawkbit.repository.model.Action.Status;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetAssignmentResult;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.domain.SliceImpl;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
import io.qameta.allure.Description;
|
||||
import io.qameta.allure.Feature;
|
||||
import io.qameta.allure.Step;
|
||||
import io.qameta.allure.Story;
|
||||
|
||||
/**
|
||||
* Test class for {@link AutoAssignChecker}.
|
||||
*
|
||||
*/
|
||||
@Feature("Component Tests - Repository")
|
||||
@Feature("Unit Tests - Repository")
|
||||
@Story("Auto assign checker")
|
||||
class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AutoAssignCheckerTest {
|
||||
@Mock
|
||||
private TargetFilterQueryManagement targetFilterQueryManagement;
|
||||
@Mock
|
||||
private TargetManagement targetManagement;
|
||||
@Mock
|
||||
private DeploymentManagement deploymentManagement;
|
||||
@Mock
|
||||
private PlatformTransactionManager transactionManager;
|
||||
@Mock
|
||||
private TenantAware tenantAware;
|
||||
|
||||
@Autowired
|
||||
private AutoAssignChecker autoAssignChecker;
|
||||
private AutoAssignChecker sut;
|
||||
|
||||
@Autowired
|
||||
private ActionRepository actionRepository;
|
||||
|
||||
@Test
|
||||
@Description("Verifies that a running action is auto canceled by a AutoAssignment which assigns another distribution-set.")
|
||||
void autoAssignDistributionSetAndAutoCloseOldActions() {
|
||||
|
||||
tenantConfigurationManagement
|
||||
.addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, true);
|
||||
|
||||
try {
|
||||
final String knownControllerId = "controller12345";
|
||||
final DistributionSet firstDistributionSet = testdataFactory.createDistributionSet();
|
||||
final DistributionSet secondDistributionSet = testdataFactory.createDistributionSet("second");
|
||||
testdataFactory.createTarget(knownControllerId);
|
||||
final DistributionSetAssignmentResult assignmentResult = assignDistributionSet(firstDistributionSet.getId(),
|
||||
knownControllerId);
|
||||
final Long manuallyAssignedActionId = getFirstAssignedActionId(assignmentResult);
|
||||
|
||||
// target filter query that matches all targets
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
|
||||
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*"));
|
||||
targetFilterQueryManagement.updateAutoAssignDS(entityFactory.targetFilterQuery()
|
||||
.updateAutoAssign(targetFilterQuery.getId()).ds(secondDistributionSet.getId()));
|
||||
// Run the check
|
||||
autoAssignChecker.check();
|
||||
|
||||
// verify that manually created action is canceled and action
|
||||
// created from AutoAssign is running
|
||||
final List<Action> actionsByKnownTarget = deploymentManagement.findActionsByTarget(knownControllerId, PAGE)
|
||||
.getContent();
|
||||
// should be 2 actions, one manually and one from the AutoAssign
|
||||
assertThat(actionsByKnownTarget).hasSize(2);
|
||||
// verify that manually assigned action is still running
|
||||
assertThat(deploymentManagement.findAction(manuallyAssignedActionId).get().getStatus())
|
||||
.isEqualTo(Status.CANCELED);
|
||||
// verify that AutoAssign created action is running
|
||||
final Action rolloutCreatedAction = actionsByKnownTarget.stream()
|
||||
.filter(action -> !action.getId().equals(manuallyAssignedActionId)).findAny().get();
|
||||
assertThat(rolloutCreatedAction.getStatus()).isEqualTo(Status.RUNNING);
|
||||
assertThat(rolloutCreatedAction.getActionType()).isEqualTo(ActionType.FORCED);
|
||||
} finally {
|
||||
tenantConfigurationManagement
|
||||
.addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, false);
|
||||
}
|
||||
@BeforeEach
|
||||
void before() {
|
||||
sut = new AutoAssignChecker(targetFilterQueryManagement, targetManagement, deploymentManagement,
|
||||
transactionManager, tenantAware);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test auto assignment of a DS to filtered targets")
|
||||
void checkAutoAssign() {
|
||||
@Description("Single device check triggers update for matching auto assignment filter.")
|
||||
void checkForDevice() {
|
||||
mockRunningAsNonSystem();
|
||||
final String target = getRandomString();
|
||||
final long ds = getRandomLong();
|
||||
final TargetFilterQuery matching = mockFilterQuery(ds);
|
||||
final TargetFilterQuery notMatching = mockFilterQuery(ds);
|
||||
when(targetFilterQueryManagement.findWithAutoAssignDS(any()))
|
||||
.thenReturn(new SliceImpl<>(Arrays.asList(notMatching, matching)));
|
||||
|
||||
final DistributionSet setA = testdataFactory.createDistributionSet("dsA"); // will
|
||||
// be
|
||||
// auto
|
||||
// assigned
|
||||
final DistributionSet setB = testdataFactory.createDistributionSet("dsB");
|
||||
when(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target, ds, matching.getQuery()))
|
||||
.thenReturn(true);
|
||||
when(targetManagement.isTargetMatchingQueryAndDSNotAssignedAndCompatible(target, ds,
|
||||
notMatching.getQuery())).thenReturn(false);
|
||||
|
||||
// target filter query that matches all targets
|
||||
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement.updateAutoAssignDS(entityFactory
|
||||
.targetFilterQuery()
|
||||
.updateAutoAssign(targetFilterQueryManagement
|
||||
.create(entityFactory.targetFilterQuery().create().name("filterA").query("name==*")).getId())
|
||||
.ds(setA.getId()));
|
||||
sut.checkSingleTarget(target);
|
||||
|
||||
final String targetDsAIdPref = "targ";
|
||||
final List<Target> targets = testdataFactory.createTargets(25, targetDsAIdPref,
|
||||
targetDsAIdPref.concat(" description"));
|
||||
final int targetsCount = targets.size();
|
||||
|
||||
// assign set A to first 10 targets
|
||||
assignDistributionSet(setA, targets.subList(0, 10));
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setA, targets.subList(0, 10), targetsCount);
|
||||
|
||||
// assign set B to first 5 targets
|
||||
// they have now 2 DS in their action history and should not get updated
|
||||
// with dsA
|
||||
assignDistributionSet(setB, targets.subList(0, 5));
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount);
|
||||
|
||||
// assign set B to next 10 targets
|
||||
assignDistributionSet(setB, targets.subList(10, 20));
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(10, 20), targetsCount);
|
||||
|
||||
// Count the number of targets that will be assigned with setA
|
||||
assertThat(targetManagement.countByRsqlAndNonDSAndCompatible(setA.getId(), targetFilterQuery.getQuery()))
|
||||
.isEqualTo(15);
|
||||
|
||||
// Run the check
|
||||
autoAssignChecker.check();
|
||||
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setA, targets.subList(5, 25), targetsCount);
|
||||
|
||||
// first 5 should keep their dsB, because they already had the dsA once
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount);
|
||||
|
||||
verifyThatCreatedActionsAreInitiatedByCurrentUser(targetFilterQuery, setA, targets);
|
||||
verify(deploymentManagement).assignDistributionSets(eq(matching.getAutoAssignInitiatedBy()),
|
||||
Mockito.argThat(deployReqMatcher(target, ds)), any());
|
||||
Mockito.verifyNoMoreInteractions(deploymentManagement);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test auto assignment of an incomplete DS to filtered targets, that causes failures")
|
||||
void checkAutoAssignWithFailures() {
|
||||
|
||||
// incomplete distribution set that will be assigned
|
||||
final DistributionSet setF = distributionSetManagement.create(entityFactory.distributionSet().create()
|
||||
.name("dsA").version("1").type(testdataFactory.findOrCreateDefaultTestDsType()));
|
||||
final DistributionSet setA = testdataFactory.createDistributionSet("dsA");
|
||||
final DistributionSet setB = testdataFactory.createDistributionSet("dsB");
|
||||
|
||||
final String targetDsAIdPref = "targA";
|
||||
final String targetDsFIdPref = "targB";
|
||||
|
||||
// target filter query that matches first bunch of targets, that should
|
||||
// fail
|
||||
assertThatExceptionOfType(IncompleteDistributionSetException.class).isThrownBy(() -> {
|
||||
final Long filterId = targetFilterQueryManagement.create(
|
||||
entityFactory.targetFilterQuery().create().name("filterA").query("id==" + targetDsFIdPref + "*"))
|
||||
.getId();
|
||||
targetFilterQueryManagement
|
||||
.updateAutoAssignDS(entityFactory.targetFilterQuery().updateAutoAssign(filterId).ds(setF.getId()));
|
||||
});
|
||||
// target filter query that matches failed bunch of targets
|
||||
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("filterB")
|
||||
.query("id==" + targetDsAIdPref + "*").autoAssignDistributionSet(setA.getId()));
|
||||
|
||||
final List<Target> targetsF = testdataFactory.createTargets(10, targetDsFIdPref,
|
||||
targetDsFIdPref.concat(" description"));
|
||||
|
||||
final List<Target> targetsA = testdataFactory.createTargets(10, targetDsAIdPref,
|
||||
targetDsAIdPref.concat(" description"));
|
||||
|
||||
final int targetsCount = targetsA.size() + targetsF.size();
|
||||
|
||||
// assign set B to first 5 targets of fail group
|
||||
assignDistributionSet(setB, targetsF.subList(0, 5));
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setB, targetsF.subList(0, 5), targetsCount);
|
||||
|
||||
// Run the check
|
||||
autoAssignChecker.check();
|
||||
|
||||
// first 5 targets of the fail group should still have setB
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setB, targetsF.subList(0, 5), targetsCount);
|
||||
|
||||
// all targets of A group should have received setA
|
||||
verifyThatTargetsHaveDistributionSetAssignment(setA, targetsA, targetsCount);
|
||||
|
||||
private ArgumentMatcher<List<DeploymentRequest>> deployReqMatcher(final String target, final long ds) {
|
||||
return requests -> {
|
||||
final DeploymentRequest request = requests.get(0);
|
||||
return requests.size() == 1 && request.getDistributionSetId() == ds && request.getControllerId() == target;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param set
|
||||
* the expected distribution set
|
||||
* @param targets
|
||||
* the targets that should have it
|
||||
*/
|
||||
@Step
|
||||
private void verifyThatTargetsHaveDistributionSetAssignment(final DistributionSet set, final List<Target> targets,
|
||||
final int count) {
|
||||
final List<Long> targetIds = targets.stream().map(Target::getId).collect(Collectors.toList());
|
||||
|
||||
final Slice<Target> targetsAll = targetManagement.findAll(PAGE);
|
||||
assertThat(targetsAll).as("Count of targets").hasSize(count);
|
||||
|
||||
for (final Target target : targetsAll) {
|
||||
if (targetIds.contains(target.getId())) {
|
||||
assertThat(deploymentManagement.getAssignedDistributionSet(target.getControllerId()).get())
|
||||
.as("assigned DS").isEqualTo(set);
|
||||
}
|
||||
}
|
||||
|
||||
private static TargetFilterQuery mockFilterQuery(final long dsId) {
|
||||
final DistributionSet ds = mock(DistributionSet.class);
|
||||
when(ds.getId()).thenReturn(dsId);
|
||||
final TargetFilterQuery filter = mock(TargetFilterQuery.class);
|
||||
when(filter.getId()).thenReturn(getRandomLong());
|
||||
when(filter.getQuery()).thenReturn(getRandomString());
|
||||
lenient().when(filter.getAutoAssignInitiatedBy()).thenReturn(getRandomString());
|
||||
when(filter.getAutoAssignDistributionSet()).thenReturn(ds);
|
||||
return filter;
|
||||
}
|
||||
|
||||
@Step
|
||||
private void verifyThatCreatedActionsAreInitiatedByCurrentUser(final TargetFilterQuery targetFilterQuery,
|
||||
final DistributionSet distributionSet, final List<Target> targets) {
|
||||
final Set<String> targetIds = targets.stream().map(Target::getControllerId).collect(Collectors.toSet());
|
||||
|
||||
actionRepository.findByDistributionSetId(Pageable.unpaged(), distributionSet.getId()).stream()
|
||||
.filter(a -> targetIds.contains(a.getTarget().getControllerId()))
|
||||
.forEach(a -> assertThat(a.getInitiatedBy())
|
||||
.as("Action should be initiated by the user who initiated the auto assignment")
|
||||
.isEqualTo(targetFilterQuery.getAutoAssignInitiatedBy()));
|
||||
private void mockRunningAsNonSystem() {
|
||||
when(tenantAware.getCurrentUsername()).thenReturn(getRandomString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Test auto assignment of a distribution set with FORCED, SOFT and DOWNLOAD_ONLY action types")
|
||||
void checkAutoAssignWithDifferentActionTypes() {
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
final String targetDsAIdPref = "A";
|
||||
final String targetDsBIdPref = "B";
|
||||
final String targetDsCIdPref = "C";
|
||||
|
||||
final List<Target> targetsA = createTargetsAndAutoAssignDistSet(targetDsAIdPref, 5, distributionSet,
|
||||
ActionType.FORCED);
|
||||
final List<Target> targetsB = createTargetsAndAutoAssignDistSet(targetDsBIdPref, 10, distributionSet,
|
||||
ActionType.SOFT);
|
||||
final List<Target> targetsC = createTargetsAndAutoAssignDistSet(targetDsCIdPref, 10, distributionSet,
|
||||
ActionType.DOWNLOAD_ONLY);
|
||||
|
||||
final int targetsCount = targetsA.size() + targetsB.size() + targetsC.size();
|
||||
|
||||
autoAssignChecker.check();
|
||||
|
||||
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsA, targetsCount);
|
||||
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsB, targetsCount);
|
||||
verifyThatTargetsHaveDistributionSetAssignment(distributionSet, targetsC, targetsCount);
|
||||
|
||||
verifyThatTargetsHaveAssignmentActionType(ActionType.FORCED, targetsA);
|
||||
verifyThatTargetsHaveAssignmentActionType(ActionType.SOFT, targetsB);
|
||||
verifyThatTargetsHaveAssignmentActionType(ActionType.DOWNLOAD_ONLY, targetsC);
|
||||
private static long getRandomLong() {
|
||||
return ThreadLocalRandom.current().nextLong();
|
||||
}
|
||||
|
||||
@Step
|
||||
private List<Target> createTargetsAndAutoAssignDistSet(final String prefix, final int targetCount,
|
||||
final DistributionSet distributionSet, final ActionType actionType) {
|
||||
|
||||
final List<Target> targets = testdataFactory.createTargets(targetCount, "target" + prefix,
|
||||
prefix.concat(" description"));
|
||||
targetFilterQueryManagement.create(
|
||||
entityFactory.targetFilterQuery().create().name("filter" + prefix).query("id==target" + prefix + "*")
|
||||
.autoAssignDistributionSet(distributionSet).autoAssignActionType(actionType));
|
||||
|
||||
return targets;
|
||||
private static String getRandomString() {
|
||||
return UUID.randomUUID().toString();
|
||||
}
|
||||
|
||||
@Step
|
||||
private void verifyThatTargetsHaveAssignmentActionType(final ActionType actionType, final List<Target> targets) {
|
||||
final List<Action> actions = targets.stream().map(Target::getControllerId).flatMap(
|
||||
controllerId -> deploymentManagement.findActionsByTarget(controllerId, PAGE).getContent().stream())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
assertThat(actions).hasSize(targets.size());
|
||||
assertThat(actions).allMatch(action -> action.getActionType().equals(actionType));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("An auto assignment target filter with weight creates actions with weights")
|
||||
void actionsWithWeightAreCreated() throws Exception {
|
||||
final int amountOfTargets = 5;
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||
final int weight = 32;
|
||||
enableMultiAssignments();
|
||||
|
||||
targetFilterQueryManagement.create(entityFactory.targetFilterQuery().create().name("a").query("name==*")
|
||||
.autoAssignDistributionSet(ds).autoAssignWeight(weight));
|
||||
testdataFactory.createTargets(amountOfTargets);
|
||||
autoAssignChecker.check();
|
||||
|
||||
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
|
||||
assertThat(actions).hasSize(amountOfTargets);
|
||||
assertThat(actions).allMatch(action -> action.getWeight().get() == weight);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("An auto assignment target filter without weight still works after multi assignment is enabled")
|
||||
void filterWithoutWeightWorksInMultiAssignmentMode() throws Exception {
|
||||
final int amountOfTargets = 5;
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet();
|
||||
targetFilterQueryManagement.create(
|
||||
entityFactory.targetFilterQuery().create().name("a").query("name==*").autoAssignDistributionSet(ds));
|
||||
enableMultiAssignments();
|
||||
|
||||
testdataFactory.createTargets(amountOfTargets);
|
||||
autoAssignChecker.check();
|
||||
|
||||
final List<Action> actions = deploymentManagement.findActionsAll(PAGE).getContent();
|
||||
assertThat(actions).hasSize(amountOfTargets);
|
||||
assertThat(actions).allMatch(action -> !action.getWeight().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Description("Verifies an auto assignment only creates actions for compatible targets")
|
||||
void checkAutoAssignmentWithIncompatibleTargets() {
|
||||
final int TARGET_COUNT = 5;
|
||||
|
||||
final DistributionSet testDs = testdataFactory.createDistributionSet();
|
||||
final DistributionSetType incompatibleDsType1 = testdataFactory
|
||||
.findOrCreateDistributionSetType("incompatibleDsType1", "incompDsType1");
|
||||
final DistributionSetType incompatibleDsType2 = testdataFactory
|
||||
.findOrCreateDistributionSetType("incompatibleDsType2", "incompDsType2");
|
||||
final TargetFilterQuery testFilter = targetFilterQueryManagement.create(entityFactory.targetFilterQuery()
|
||||
.create().name("test-filter").query("name==*").autoAssignDistributionSet(testDs));
|
||||
|
||||
final TargetType incompatibleEmptyType = testdataFactory.createTargetType("incompatibleEmptyType",
|
||||
Collections.emptyList());
|
||||
final TargetType incompatibleSingleType = testdataFactory.createTargetType("incompatibleSingleType",
|
||||
Collections.singletonList(incompatibleDsType1));
|
||||
final TargetType incompatibleMultiType = testdataFactory.createTargetType("incompatibleMultiType",
|
||||
Arrays.asList(incompatibleDsType1, incompatibleDsType2));
|
||||
final TargetType compatibleSingleType = testdataFactory.createTargetType("compatibleSingleType",
|
||||
Collections.singletonList(testDs.getType()));
|
||||
final TargetType compatibleMultiType = testdataFactory.createTargetType("compatibleMultiType",
|
||||
Arrays.asList(testDs.getType(), incompatibleDsType1));
|
||||
|
||||
testdataFactory.createTargetsWithType(TARGET_COUNT, "incompatibleEmpty", incompatibleEmptyType);
|
||||
testdataFactory.createTargetsWithType(TARGET_COUNT, "incompatibleSingle", incompatibleSingleType);
|
||||
testdataFactory.createTargetsWithType(TARGET_COUNT, "incompatibleMulti", incompatibleMultiType);
|
||||
|
||||
final List<Target> compatibleTargetsSingleType = testdataFactory.createTargetsWithType(TARGET_COUNT,
|
||||
"compatibleSingle", compatibleSingleType);
|
||||
final List<Target> compatibleTargetsMultiType = testdataFactory.createTargetsWithType(TARGET_COUNT,
|
||||
"compatibleMulti", compatibleMultiType);
|
||||
final List<Target> compatibleTargetsWithoutType = testdataFactory.createTargets(TARGET_COUNT,
|
||||
"compatibleSingleWithoutType");
|
||||
|
||||
final List<Long> compatibleTargets = Stream
|
||||
.of(compatibleTargetsSingleType, compatibleTargetsMultiType, compatibleTargetsWithoutType)
|
||||
.flatMap(Collection::stream).map(Target::getId).collect(Collectors.toList());
|
||||
final long compatibleCount = targetManagement.countByRsqlAndNonDSAndCompatible(testDs.getId(),
|
||||
testFilter.getQuery());
|
||||
assertThat(compatibleCount).isEqualTo(compatibleTargets.size());
|
||||
|
||||
autoAssignChecker.check();
|
||||
|
||||
final List<Action> actions = deploymentManagement.findActionsAll(Pageable.unpaged()).getContent();
|
||||
assertThat(actions).hasSize(compatibleTargets.size());
|
||||
final List<Long> actionTargets = actions.stream().map(a -> a.getTarget().getId()).collect(Collectors.toList());
|
||||
assertThat(actionTargets).containsExactlyInAnyOrderElementsOf(compatibleTargets);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user