Merge branch 'master' into feature_target_filtering_supports_overdue

Conflicts:
	hawkbit-repository/hawkbit-repository-jpa/src/main/java/org/eclipse/hawkbit/RepositoryApplicationConfiguration.java
	hawkbit-ui/src/main/resources/messages_de.properties
	hawkbit-ui/src/main/resources/messages_en.properties
This commit is contained in:
Dominik Herbst
2016-10-13 10:46:00 +02:00
70 changed files with 3533 additions and 1305 deletions

View File

@@ -42,6 +42,8 @@ import org.eclipse.hawkbit.repository.jpa.JpaTargetManagement;
import org.eclipse.hawkbit.repository.jpa.JpaTenantConfigurationManagement;
import org.eclipse.hawkbit.repository.jpa.JpaTenantStatsManagement;
import org.eclipse.hawkbit.repository.jpa.aspects.ExceptionMappingAspectHandler;
import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignChecker;
import org.eclipse.hawkbit.repository.jpa.autoassign.AutoAssignScheduler;
import org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager;
import org.eclipse.hawkbit.repository.jpa.model.helper.AfterTransactionCommitExecutorHolder;
import org.eclipse.hawkbit.repository.jpa.model.helper.CacheManagerHolder;
@@ -399,4 +401,45 @@ public class RepositoryApplicationConfiguration extends JpaBaseConfiguration {
return new JpaEntityFactory();
}
/**
* {@link AutoAssignChecker} bean.
*
* @param targetFilterQueryManagement
* to get all target filter queries
* @param targetManagement
* to get targets
* @param deploymentManagement
* to assign distribution sets to targets
* @param transactionManager
* to run transactions
* @return a new {@link AutoAssignChecker}
*/
@Bean
@ConditionalOnMissingBean
public AutoAssignChecker autoAssignChecker(TargetFilterQueryManagement targetFilterQueryManagement,
TargetManagement targetManagement, DeploymentManagement deploymentManagement,
PlatformTransactionManager transactionManager) {
return new AutoAssignChecker(targetFilterQueryManagement, targetManagement, deploymentManagement,
transactionManager);
}
/**
* {@link AutoAssignScheduler} bean.
*
* @param tenantAware
* to run as specific tenant
* @param systemManagement
* to find all tenants
* @param systemSecurityContext
* to run as system
* @param autoAssignChecker
* to run a check as tenant
* @return a new {@link AutoAssignChecker}
*/
@Bean
@ConditionalOnMissingBean
public AutoAssignScheduler autoAssignScheduler(TenantAware tenantAware, SystemManagement systemManagement,
SystemSecurityContext systemSecurityContext, AutoAssignChecker autoAssignChecker) {
return new AutoAssignScheduler(tenantAware, systemManagement, systemSecurityContext, autoAssignChecker);
}
}

View File

@@ -179,13 +179,23 @@ public class JpaDeploymentManagement implements DeploymentManagement {
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
final Collection<TargetWithActionType> targets) {
return assignDistributionSet(dsID, targets, null);
}
@Override
@Modifying
@Transactional(isolation = Isolation.READ_COMMITTED)
@CacheEvict(value = { "distributionUsageAssigned" }, allEntries = true)
public DistributionSetAssignmentResult assignDistributionSet(final Long dsID,
final Collection<TargetWithActionType> targets,
final String actionMessage) {
final JpaDistributionSet set = distributoinSetRepository.findOne(dsID);
if (set == null) {
throw new EntityNotFoundException(
String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID));
}
return assignDistributionSetToTargets(set, targets, null, null);
return assignDistributionSetToTargets(set, targets, null, null, actionMessage);
}
@Override
@@ -200,21 +210,23 @@ public class JpaDeploymentManagement implements DeploymentManagement {
String.format("no %s with id %d found", DistributionSet.class.getSimpleName(), dsID));
}
return assignDistributionSetToTargets(set, targets, (JpaRollout) rollout, (JpaRolloutGroup) rolloutGroup);
return assignDistributionSetToTargets(set, targets, (JpaRollout) rollout, (JpaRolloutGroup) rolloutGroup, null);
}
/**
* method assigns the {@link DistributionSet} to all {@link Target}s by
* their IDs with a specific {@link ActionType} and {@code forcetime}.
*
* @param dsID
* @param set
* the ID of the distribution set to assign
* @param targets
* @param targetsWithActionType
* a list of all targets and their action type
* @param rollout
* the rollout for this assignment
* @param rolloutGroup
* the rollout group for this assignment
* @param actionMessage
* an optional message to be written into the action status
* @return the assignment result
*
* @throw IncompleteDistributionSetException if mandatory
@@ -223,7 +235,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
*/
private DistributionSetAssignmentResult assignDistributionSetToTargets(@NotNull final JpaDistributionSet set,
final Collection<TargetWithActionType> targetsWithActionType, final JpaRollout rollout,
final JpaRolloutGroup rolloutGroup) {
final JpaRolloutGroup rolloutGroup, final String actionMessage) {
if (!set.isComplete()) {
throw new IncompleteDistributionSetException(
@@ -296,6 +308,9 @@ public class JpaDeploymentManagement implements DeploymentManagement {
actionStatus.setAction(action);
actionStatus.setOccurredAt(action.getCreatedAt());
actionStatus.setStatus(Status.RUNNING);
if(actionMessage != null) {
actionStatus.addMessage(actionMessage);
}
actionStatusRepository.save(actionStatus);
});
@@ -350,7 +365,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
* the Target which has been assigned to a distribution set
* @param actionId
* the action id of the assignment
* @param softwareModules
* @param modules
* the software modules which have been assigned
*/
private void assignDistributionSetEvent(final JpaTarget target, final Long actionId,
@@ -367,11 +382,11 @@ public class JpaDeploymentManagement implements DeploymentManagement {
}
/**
* Removes {@link UpdateAction}s that are no longer necessary and sends
* Removes {@link Action}s that are no longer necessary and sends
* cancellations to the controller.
*
* @param myTarget
* to override {@link UpdateAction}s
* @param targetsIds
* to override {@link Action}s
*/
private Set<Long> overrideObsoleteUpdateActions(final List<Long> targetsIds) {
@@ -406,7 +421,7 @@ public class JpaDeploymentManagement implements DeploymentManagement {
return assignDistributionSetToTargets(set, tIDs.stream()
.map(t -> new TargetWithActionType(t, actionType, forcedTime)).collect(Collectors.toList()), null,
null);
null, null);
}
@Override

View File

@@ -96,6 +96,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
@Autowired
private DistributionSetMetadataRepository distributionSetMetadataRepository;
@Autowired
private TargetFilterQueryRepository targetFilterQueryRepository;
@Autowired
private ActionRepository actionRepository;
@@ -182,7 +185,9 @@ public class JpaDistributionSetManagement implements DistributionSetManagement {
// soft delete assigned
if (!assigned.isEmpty()) {
distributionSetRepository.deleteDistributionSet(assigned.toArray(new Long[assigned.size()]));
Long[] dsIds = assigned.toArray(new Long[assigned.size()]);
distributionSetRepository.deleteDistributionSet(dsIds);
targetFilterQueryRepository.unsetAutoAssignDistributionSet(dsIds);
}
// mark the rest as hard delete

View File

@@ -134,6 +134,16 @@ public class JpaEntityFactory implements EntityFactory {
return new JpaTargetFilterQuery();
}
@Override
public TargetFilterQuery generateTargetFilterQuery(String name, String query) {
return new JpaTargetFilterQuery(name, query);
}
@Override
public TargetFilterQuery generateTargetFilterQuery(String name, String query, DistributionSet autoAssignDS) {
return new JpaTargetFilterQuery(name, query, (JpaDistributionSet) autoAssignDS);
}
@Override
public SoftwareModuleType generateSoftwareModuleType() {
return new JpaSoftwareModuleType();

View File

@@ -56,6 +56,9 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
@Autowired
private TargetRepository targetRepository;
@Autowired
private TargetFilterQueryRepository targetFilterQueryRepository;
@Autowired
private ActionRepository actionRepository;
@@ -221,6 +224,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
tenantMetaDataRepository.deleteByTenantIgnoreCase(tenant);
tenantConfigurationRepository.deleteByTenantIgnoreCase(tenant);
targetRepository.deleteByTenantIgnoreCase(tenant);
targetFilterQueryRepository.deleteByTenantIgnoreCase(tenant);
actionRepository.deleteByTenantIgnoreCase(tenant);
rolloutGroupRepository.deleteByTenantIgnoreCase(tenant);
rolloutRepository.deleteByTenantIgnoreCase(tenant);

View File

@@ -9,9 +9,11 @@
package org.eclipse.hawkbit.repository.jpa;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.hawkbit.repository.TargetFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryFields;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
@@ -19,6 +21,7 @@ import org.eclipse.hawkbit.repository.jpa.rsql.RSQLUtility;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.repository.jpa.specifications.SpecificationsBuilder;
import org.eclipse.hawkbit.repository.jpa.specifications.TargetFilterQuerySpecification;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
@@ -34,6 +37,8 @@ import org.springframework.validation.annotation.Validated;
import com.google.common.base.Strings;
import javax.validation.constraints.NotNull;
/**
* JPA implementation of {@link TargetFilterQueryManagement}.
*
@@ -71,20 +76,61 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
return convertPage(targetFilterQueryRepository.findAll(pageable), pageable);
}
@Override
public Long countAllTargetFilterQuery() {
return targetFilterQueryRepository.count();
}
private static Page<TargetFilterQuery> convertPage(final Page<JpaTargetFilterQuery> findAll,
final Pageable pageable) {
return new PageImpl<>(new ArrayList<>(findAll.getContent()), pageable, findAll.getTotalElements());
}
@Override
public Page<TargetFilterQuery> findTargetFilterQueryByFilters(final Pageable pageable, final String name) {
final List<Specification<JpaTargetFilterQuery>> specList = new ArrayList<>();
public Page<TargetFilterQuery> findTargetFilterQueryByName(final Pageable pageable, final String name) {
List<Specification<JpaTargetFilterQuery>> specList = Collections.emptyList();
if (!Strings.isNullOrEmpty(name)) {
specList.add(TargetFilterQuerySpecification.likeName(name));
specList = Collections.singletonList(TargetFilterQuerySpecification.likeName(name));
}
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
}
@Override
public Page<TargetFilterQuery> findTargetFilterQueryByFilter(@NotNull Pageable pageable, String rsqlFilter) {
List<Specification<JpaTargetFilterQuery>> specList = Collections.emptyList();
if (!Strings.isNullOrEmpty(rsqlFilter)) {
specList = Collections.singletonList(
RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class, virtualPropertyReplacer));
}
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
}
@Override
public Page<TargetFilterQuery> findTargetFilterQueryByAutoAssignDS(@NotNull Pageable pageable,
DistributionSet distributionSet) {
return findTargetFilterQueryByAutoAssignDS(pageable, distributionSet, null);
}
@Override
public Page<TargetFilterQuery> findTargetFilterQueryByAutoAssignDS(@NotNull Pageable pageable,
DistributionSet distributionSet, String rsqlFilter) {
final List<Specification<JpaTargetFilterQuery>> specList = new ArrayList<>(2);
if (distributionSet != null) {
specList.add(TargetFilterQuerySpecification.byAutoAssignDS(distributionSet));
}
if (!Strings.isNullOrEmpty(rsqlFilter)) {
specList.add(RSQLUtility.parse(rsqlFilter, TargetFilterQueryFields.class, virtualPropertyReplacer));
}
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
}
@Override
public Page<TargetFilterQuery> findTargetFilterQueryWithAutoAssignDS(@NotNull Pageable pageable) {
final List<Specification<JpaTargetFilterQuery>> specList = Collections
.singletonList(TargetFilterQuerySpecification.withAutoAssignDS());
return convertPage(findTargetFilterQueryByCriteriaAPI(pageable, specList), pageable);
}
private Page<JpaTargetFilterQuery> findTargetFilterQueryByCriteriaAPI(final Pageable pageable,
final List<Specification<JpaTargetFilterQuery>> specList) {
if (specList == null || specList.isEmpty()) {

View File

@@ -26,6 +26,7 @@ import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Order;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.validation.constraints.NotNull;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.hawkbit.repository.FilterParams;
@@ -570,7 +571,8 @@ public class JpaTargetManagement implements TargetManagement {
final CriteriaQuery<Object[]> multiselect = query.multiselect(targetRoot.get(JpaTarget_.id),
targetRoot.get(JpaTarget_.controllerId), targetRoot.get(JpaTarget_.name), targetRoot.get(sortProperty));
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class, virtualPropertyReplacer);
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class,
virtualPropertyReplacer);
final Predicate[] specificationsForMultiSelect = specificationsToPredicate(Lists.newArrayList(spec), targetRoot,
multiselect, cb);
@@ -584,6 +586,33 @@ public class JpaTargetManagement implements TargetManagement {
.collect(Collectors.toList());
}
@Override
public Page<Target> findAllTargetsByTargetFilterQueryAndNonDS(@NotNull Pageable pageRequest, Long distributionSetId,
@NotNull TargetFilterQuery targetFilterQuery) {
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class,
virtualPropertyReplacer);
return findTargetsBySpec(
(root, cq,
cb) -> cb.and(spec.toPredicate(root, cq, cb), TargetSpecifications
.hasNotDistributionSetInActions(distributionSetId).toPredicate(root, cq, cb)),
pageRequest);
}
@Override
public Long countTargetsByTargetFilterQueryAndNonDS(Long distributionSetId,
@NotNull TargetFilterQuery targetFilterQuery) {
final Specification<JpaTarget> spec = RSQLUtility.parse(targetFilterQuery.getQuery(), TargetFields.class,
virtualPropertyReplacer);
final List<Specification<JpaTarget>> specList = new ArrayList<>(2);
specList.add(spec);
specList.add(TargetSpecifications.hasNotDistributionSetInActions(distributionSetId));
return countByCriteriaAPI(specList);
}
@PreDestroy
void destroy() {
eventBus.unregister(this);

View File

@@ -13,6 +13,8 @@ import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
@@ -43,4 +45,15 @@ public interface TargetFilterQueryRepository
@Transactional
<S extends JpaTargetFilterQuery> S save(S entity);
/**
* Sets the auto assign distribution sets to null which match the ds ids.
*
* @param dsIds
* distribution set ids to be set to null
*/
@Modifying
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
@Query("update JpaTargetFilterQuery d set d.autoAssignDistributionSet = NULL where d.autoAssignDistributionSet in :ids")
void unsetAutoAssignDistributionSet(@Param("ids") Long... dsIds);
}

View File

@@ -0,0 +1,179 @@
/**
* 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 java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.exception.AbstractServerRtException;
import org.eclipse.hawkbit.repository.DeploymentManagement;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.TargetManagement;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.RepositoryModelConstants;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import javax.persistence.PersistenceException;
/**
* Checks if targets need a new distribution set (DS) based on the target filter
* queries and assigns the new DS when necessary. First all target filter
* queries are listed. For every target filter query (TFQ) the auto assign DS is
* retrieved. All targets get listed per target filter query, that match the TFQ
* and that don't have the auto assign DS in their action history.
*/
public class AutoAssignChecker {
private static final Logger LOGGER = LoggerFactory.getLogger(AutoAssignChecker.class);
private final TargetFilterQueryManagement targetFilterQueryManagement;
private final TargetManagement targetManagement;
private final DeploymentManagement deploymentManagement;
private final TransactionTemplate transactionTemplate;
/**
* Maximum for target filter queries with auto assign DS Maximum for targets
* that are fetched in one turn
*/
private static final int PAGE_SIZE = 1000;
/**
* The message which is added to the action status when a distribution set
* is assigned to an target. First %s is the name of the target filter.
*/
private static final String ACTION_MESSAGE = "Auto assignment by target filter: %s";
/**
* Instantiates a new auto assign checker
*
* @param targetFilterQueryManagement
* to get all target filter queries
* @param targetManagement
* to get targets
* @param deploymentManagement
* to assign distribution sets to targets
* @param transactionManager
* to run transactions
*/
public AutoAssignChecker(TargetFilterQueryManagement targetFilterQueryManagement, TargetManagement targetManagement,
DeploymentManagement deploymentManagement, PlatformTransactionManager transactionManager) {
this.targetFilterQueryManagement = targetFilterQueryManagement;
this.targetManagement = targetManagement;
this.deploymentManagement = deploymentManagement;
final DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("autoAssignDSToTargets");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
transactionTemplate = new TransactionTemplate(transactionManager, def);
}
/**
* Checks all target filter queries with an auto assign distribution set and
* triggers the check and assignment to targets that don't have the design
* DS yet
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void check() {
PageRequest pageRequest = new PageRequest(0, PAGE_SIZE);
Page<TargetFilterQuery> filterQueries = targetFilterQueryManagement
.findTargetFilterQueryWithAutoAssignDS(pageRequest);
for (TargetFilterQuery filterQuery : filterQueries) {
checkByTargetFilterQueryAndAssignDS(filterQuery);
}
}
/**
* Fetches the distribution set, gets all controllerIds and assigns the DS
* to them. Catches PersistenceException and own exceptions derived from
* AbstractServerRtException
*
* @param targetFilterQuery
* the target filter query
*/
private void checkByTargetFilterQueryAndAssignDS(TargetFilterQuery targetFilterQuery) {
try {
DistributionSet distributionSet = targetFilterQuery.getAutoAssignDistributionSet();
int count;
do {
count = runTransactionalAssignment(targetFilterQuery, distributionSet.getId());
} while (count == PAGE_SIZE);
} catch (PersistenceException | AbstractServerRtException e) {
LOGGER.error("Error during auto assign check of target filter query " + targetFilterQuery.getId(), e);
}
}
/**
* Runs one page of target assignments within a dedicated transaction
*
* @param targetFilterQuery
* the target filter query
* @param dsId
* distribution set id to assign
* @return count of targets
*/
private int runTransactionalAssignment(TargetFilterQuery targetFilterQuery, Long dsId) {
final String actionMessage = String.format(ACTION_MESSAGE, targetFilterQuery.getName());
return transactionTemplate.execute(status -> {
List<TargetWithActionType> targets = getTargetsWithActionType(targetFilterQuery, dsId, PAGE_SIZE);
int count = targets.size();
if (count > 0) {
deploymentManagement.assignDistributionSet(dsId, targets, actionMessage);
}
return count;
});
}
/**
* Gets all matching targets with the designated action from the target
* management
*
* @param targetFilterQuery
* the query the targets have to match
* @param dsId
* dsId the targets are not allowed to have in their action
* history
* @param count
* maximum amount of targets to retrieve
* @return list of targets with action type
*/
private List<TargetWithActionType> getTargetsWithActionType(TargetFilterQuery targetFilterQuery, Long dsId,
int count) {
Page<Target> targets = targetManagement.findAllTargetsByTargetFilterQueryAndNonDS(new PageRequest(0, count),
dsId, targetFilterQuery);
return targets.getContent().stream().map(t -> new TargetWithActionType(t.getControllerId(),
Action.ActionType.FORCED, RepositoryModelConstants.NO_FORCE_TIME)).collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,92 @@
/**
* 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 java.util.List;
import org.eclipse.hawkbit.repository.AutoAssignProperties;
import org.eclipse.hawkbit.repository.SystemManagement;
import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.tenancy.TenantAware;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.Scheduled;
/**
* Scheduler to check target filters for auto assignment of distribution sets
*/
// don't active the auto assign scheduler in test, otherwise it is hard to test
@Profile("!test")
@EnableConfigurationProperties(AutoAssignProperties.class)
public class AutoAssignScheduler {
private static final Logger LOGGER = LoggerFactory.getLogger(AutoAssignScheduler.class);
private final TenantAware tenantAware;
private final SystemManagement systemManagement;
private final SystemSecurityContext systemSecurityContext;
private final AutoAssignChecker autoAssignChecker;
/**
* Instantiates a new AutoAssignScheduler
*
* @param tenantAware
* to run as specific tenant
* @param systemManagement
* to find all tenants
* @param systemSecurityContext
* to run as system
* @param autoAssignChecker
* to run a check as tenant
*/
public AutoAssignScheduler(TenantAware tenantAware, SystemManagement systemManagement,
SystemSecurityContext systemSecurityContext, AutoAssignChecker autoAssignChecker) {
this.tenantAware = tenantAware;
this.systemManagement = systemManagement;
this.systemSecurityContext = systemSecurityContext;
this.autoAssignChecker = autoAssignChecker;
}
/**
* Scheduler method called by the spring-async mechanism. Retrieves all
* tenants from the {@link SystemManagement#findTenants()} and runs for each
* tenant the auto assignments defined in the target filter queries
* {@link SystemSecurityContext}.
*/
@Scheduled(initialDelayString = AutoAssignProperties.Scheduler.PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = AutoAssignProperties.Scheduler.PROP_SCHEDULER_DELAY_PLACEHOLDER)
public void autoAssignScheduler() {
LOGGER.debug("auto assign schedule checker has been triggered.");
// run this code in system code privileged to have the necessary
// permission to query and create entities.
systemSecurityContext.runAsSystem(() -> {
// workaround eclipselink that is currently not possible to
// execute a query without multitenancy if MultiTenant
// annotation is used.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
// iterate through all tenants and execute the rollout check for
// each tenant separately.
final List<String> tenants = systemManagement.findTenants();
LOGGER.info("Checking target filter queries for tenants: {}", tenants.size());
for (final String tenant : tenants) {
tenantAware.runAsTenant(tenant, () -> {
autoAssignChecker.check();
return null;
});
}
return null;
});
}
}

View File

@@ -50,6 +50,7 @@ import org.eclipse.hawkbit.repository.model.DistributionSetType;
import org.eclipse.hawkbit.repository.model.SoftwareModule;
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetInfo;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.descriptors.DescriptorEvent;
@@ -95,6 +96,9 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
@OneToMany(mappedBy = "assignedDistributionSet", targetEntity = JpaTarget.class, fetch = FetchType.LAZY)
private List<Target> assignedToTargets;
@OneToMany(mappedBy = "autoAssignDistributionSet", targetEntity = JpaTargetFilterQuery.class, fetch = FetchType.LAZY)
private List<TargetFilterQuery> autoAssignFilters;
@OneToMany(mappedBy = "installedDistributionSet", targetEntity = JpaTargetInfo.class, fetch = FetchType.LAZY)
private List<TargetInfo> installedAtTargets;
@@ -224,6 +228,11 @@ public class JpaDistributionSet extends AbstractJpaNamedVersionedEntity implemen
return Collections.unmodifiableList(assignedToTargets);
}
@Override
public List<TargetFilterQuery> getAutoAssignFilters() {
return autoAssignFilters;
}
@Override
public List<TargetInfo> getInstalledTargets() {
if (installedAtTargets == null) {

View File

@@ -9,13 +9,19 @@
package org.eclipse.hawkbit.repository.jpa.model;
import javax.persistence.Column;
import javax.persistence.ConstraintMode;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
/**
@@ -42,6 +48,10 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity imple
@NotNull
private String query;
@ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = JpaDistributionSet.class)
@JoinColumn(name = "auto_assign_distribution_set", nullable = true, foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "fk_filter_auto_assign_ds"))
private JpaDistributionSet autoAssignDistributionSet;
public JpaTargetFilterQuery() {
// Default constructor for JPA.
}
@@ -59,6 +69,22 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity imple
this.query = query;
}
/**
* Construct a Target filter query with auto assign distribution set
*
* @param name
* of the {@link TargetFilterQuery}.
* @param query
* of the {@link TargetFilterQuery}.
* @param autoAssignDistributionSet
* of the {@link TargetFilterQuery}.
*/
public JpaTargetFilterQuery(String name, String query, JpaDistributionSet autoAssignDistributionSet) {
this.name = name;
this.query = query;
this.autoAssignDistributionSet = autoAssignDistributionSet;
}
@Override
public String getName() {
return name;
@@ -78,4 +104,14 @@ public class JpaTargetFilterQuery extends AbstractJpaTenantAwareBaseEntity imple
public void setQuery(final String query) {
this.query = query;
}
@Override
public DistributionSet getAutoAssignDistributionSet() {
return autoAssignDistributionSet;
}
@Override
public void setAutoAssignDistributionSet(DistributionSet distributionSet) {
autoAssignDistributionSet = (JpaDistributionSet)distributionSet;
}
}

View File

@@ -28,7 +28,7 @@ public final class SpecificationsBuilder {
* where clause.
*
* @param specList
* all specification wich will combine
* all specification which will combine
* @return <null> if the given specification list is empty
*/
public static <T> Specifications<T> combineWithAnd(final List<Specification<T>> specList) {
@@ -36,8 +36,7 @@ public final class SpecificationsBuilder {
return null;
}
Specifications<T> specs = Specifications.where(specList.get(0));
specList.remove(0);
for (final Specification<T> specification : specList) {
for (final Specification<T> specification : specList.subList(1, specList.size())) {
specs = specs.and(specification);
}
return specs;

View File

@@ -10,6 +10,7 @@ package org.eclipse.hawkbit.repository.jpa.specifications;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery_;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.springframework.data.jpa.domain.Specification;
@@ -37,4 +38,28 @@ public final class TargetFilterQuerySpecification {
return cb.like(cb.lower(targetFilterQueryRoot.get(JpaTargetFilterQuery_.name)), searchTextToLower);
};
}
/**
* {@link Specification} for retrieving {@link JpaTargetFilterQuery}s based
* on is {@link JpaTargetFilterQuery#getName()}.
*
* @param distributionSet
* of the filter
* @return the {@link JpaTargetFilterQuery} {@link Specification}
*/
public static Specification<JpaTargetFilterQuery> byAutoAssignDS(final DistributionSet distributionSet) {
return (targetFilterQueryRoot, query, cb) -> cb
.equal(targetFilterQueryRoot.get(JpaTargetFilterQuery_.autoAssignDistributionSet), distributionSet);
}
/**
* {@link Specification} for retrieving {@link JpaTargetFilterQuery}s based
* on is {@link JpaTargetFilterQuery#getName()}.
*
* @return the {@link JpaTargetFilterQuery} {@link Specification}
*/
public static Specification<JpaTargetFilterQuery> withAutoAssignDS() {
return (targetFilterQueryRoot, query, cb) -> cb
.isNotNull(targetFilterQueryRoot.get(JpaTargetFilterQuery_.autoAssignDistributionSet));
}
}

View File

@@ -14,12 +14,15 @@ import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.SetJoin;
import javax.validation.constraints.NotNull;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet_;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
@@ -234,6 +237,24 @@ public final class TargetSpecifications {
distributionSetId);
}
/**
* {@link Specification} for retrieving {@link Target}s that don't have the given
* distribution set in their action history
*
* @param distributionSetId
* the ID of the distribution set which must not be assigned
* @return the {@link Target} {@link Specification}
*/
public static Specification<JpaTarget> hasNotDistributionSetInActions(final Long distributionSetId) {
return (targetRoot, query, cb) -> {
final ListJoin<JpaTarget, JpaAction> actionsJoin = targetRoot.join(JpaTarget_.actions, JoinType.LEFT);
actionsJoin.on(cb.equal(actionsJoin.get(JpaAction_.distributionSet).get(JpaDistributionSet_.id),
distributionSetId));
return cb.isNull(actionsJoin.get(JpaAction_.id));
};
}
/**
* {@link Specification} for retrieving {@link Target}s by assigned
* distribution set.

View File

@@ -0,0 +1,8 @@
ALTER TABLE sp_target_filter_query
ADD column auto_assign_distribution_set BIGINT;
ALTER TABLE sp_target_filter_query
ADD CONSTRAINT fk_filter_auto_assign_ds
FOREIGN KEY (auto_assign_distribution_set)
REFERENCES sp_distribution_set
ON DELETE SET NULL;

View File

@@ -0,0 +1,8 @@
ALTER TABLE sp_target_filter_query
ADD COLUMN auto_assign_distribution_set BIGINT;
ALTER TABLE sp_target_filter_query
ADD CONSTRAINT fk_filter_auto_assign_ds
FOREIGN KEY (auto_assign_distribution_set)
REFERENCES sp_distribution_set (id)
ON DELETE SET NULL;

View File

@@ -0,0 +1,257 @@
/**
* 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;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
import java.util.Iterator;
import java.util.List;
import static org.fest.assertions.api.Assertions.assertThat;
import static org.junit.Assert.*;
/**
* Test class for {@link TargetFilterQueryManagement}.
*
*/
@Features("Component Tests - Repository")
@Stories("Target Filter Query Management")
public class TargetFilterQueryManagementTest extends AbstractJpaIntegrationTest {
@Test
@Description("Test creation of target filter query.")
public void createTargetFilterQuery() {
final String filterName = "new target filter";
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
assertEquals("Retrieved newly created custom target filter", targetFilterQuery,
targetFilterQueryManagement.findTargetFilterQueryByName(filterName));
}
@Test
@Description("Test searching a target filter query.")
public void searchTargetFilterQuery() {
final String filterName = "targetFilterQueryName";
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery("someOtherFilter", "name==PendingTargets002"));
List<TargetFilterQuery> results = targetFilterQueryManagement
.findTargetFilterQueryByFilter(new PageRequest(0, 10), "name==" + filterName).getContent();
assertEquals("Search result should have 1 result", 1, results.size());
assertEquals("Retrieved newly created custom target filter", targetFilterQuery, results.get(0));
}
@Test(expected = RSQLParameterUnsupportedFieldException.class)
@Description("Test searching a target filter query with an invalid filter.")
public void searchTargetFilterQueryInvalidField() {
// Should throw an exception
targetFilterQueryManagement.findTargetFilterQueryByFilter(new PageRequest(0, 10), "unknownField==testValue")
.getContent();
}
@Test
@Description("Checks if the EntityAlreadyExistsException is thrown if a targetfilterquery with the same name are created more than once.")
public void createDuplicateTargetFilterQuery() {
final String filterName = "new target filter duplicate";
targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
try {
targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
fail("should not have worked as query already exists");
} catch (final EntityAlreadyExistsException e) {
}
}
@Test
@Description("Test deletion of target filter query.")
public void deleteTargetFilterQuery() {
final String filterName = "delete_target_filter_query";
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
targetFilterQueryManagement.deleteTargetFilterQuery(targetFilterQuery.getId());
assertEquals("Returns null as the target filter is deleted", null,
targetFilterQueryManagement.findTargetFilterQueryById(targetFilterQuery.getId()));
}
@Test
@Description("Test updation of target filter query.")
public void updateTargetFilterQuery() {
final String filterName = "target_filter_01";
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
final String newQuery = "status==UNKNOWN";
targetFilterQuery.setQuery(newQuery);
targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery);
assertEquals("Returns updated target filter query", newQuery,
targetFilterQueryManagement.findTargetFilterQueryByName(filterName).getQuery());
}
@Test
@Description("Test assigning a distribution set")
public void assignDistributionSet() {
final String filterName = "target_filter_02";
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
final DistributionSet distributionSet = distributionSetManagement.createDistributionSet(new JpaDistributionSet(
"dist_Set_01", "0.1", "", null, null
));
targetFilterQuery.setAutoAssignDistributionSet(distributionSet);
targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery);
TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName);
assertEquals("Returns correct distribution set", distributionSet,
tfq.getAutoAssignDistributionSet());
}
@Test
@Description("Test removing distribution set while it has a relation to a target filter query")
public void removeAssignDistributionSet() {
final String filterName = "target_filter_03";
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
final DistributionSet distributionSet = distributionSetManagement.createDistributionSet(new JpaDistributionSet(
"dist_Set_02", "0.1", "", null, null
));
targetFilterQuery.setAutoAssignDistributionSet(distributionSet);
targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery);
// Check if target filter query is there
TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName);
assertEquals("Returns correct distribution set", distributionSet,
tfq.getAutoAssignDistributionSet());
distributionSetManagement.deleteDistributionSet(distributionSet);
// Check if auto assign distribution set is null
tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName);
assertNotNull("Returns target filter query", tfq);
assertNull("Returns distribution set as null", tfq.getAutoAssignDistributionSet());
}
@Test
@Description("Test to implicitly remove the auto assign distribution set when the ds is soft deleted")
public void implicitlyRemoveAssignDistributionSet() {
final String filterName = "target_filter_03";
DistributionSet distributionSet = testdataFactory.createDistributionSet("dist_set");
Target target = testdataFactory.createTarget();
// Assign the distribution set to an target, to force a soft delete in a
// later step
deploymentManagement.assignDistributionSet(distributionSet.getId(), target.getControllerId());
targetFilterQueryManagement.createTargetFilterQuery(
new JpaTargetFilterQuery(filterName, "name==PendingTargets001", (JpaDistributionSet) distributionSet));
// Check if target filter query is there with the distribution set
TargetFilterQuery tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName);
assertEquals("Returns correct distribution set", distributionSet, tfq.getAutoAssignDistributionSet());
distributionSetManagement.deleteDistributionSet(distributionSet);
// Check if distribution set is still in the database with deleted flag
assertTrue("Distribution set should be deleted",
distributionSetManagement.findDistributionSetById(distributionSet.getId()).isDeleted());
// Check if auto assign distribution set is null
tfq = targetFilterQueryManagement.findTargetFilterQueryByName(filterName);
assertNotNull("Returns target filter query", tfq);
assertNull("Returns distribution set as null", tfq.getAutoAssignDistributionSet());
}
@Test
@Description("Test finding and auto assign distribution set")
public void findFiltersWithDistributionSet() {
final String filterName = "d";
assertEquals(0L, targetFilterQueryManagement.countAllTargetFilterQuery().longValue());
targetFilterQueryManagement.createTargetFilterQuery(new JpaTargetFilterQuery("a", "name==*"));
targetFilterQueryManagement.createTargetFilterQuery(new JpaTargetFilterQuery("b", "name==*"));
final DistributionSet distributionSet = distributionSetManagement
.createDistributionSet(new JpaDistributionSet("dist_Set_01", "0.1", "", null, null));
final DistributionSet distributionSet2 = distributionSetManagement
.createDistributionSet(new JpaDistributionSet("dist_Set_02", "0.1", "", null, null));
final TargetFilterQuery tfq = targetFilterQueryManagement.createTargetFilterQuery(
new JpaTargetFilterQuery("c", "name==x", (JpaDistributionSet) distributionSet));
final TargetFilterQuery tfq2 = targetFilterQueryManagement.createTargetFilterQuery(
new JpaTargetFilterQuery(filterName, "name==z*", (JpaDistributionSet) distributionSet2));
assertEquals(4L, targetFilterQueryManagement.countAllTargetFilterQuery().longValue());
// check if find works
Page<TargetFilterQuery> tfqList = targetFilterQueryManagement
.findTargetFilterQueryByAutoAssignDS(new PageRequest(0, 500), distributionSet);
assertThat(1L).as("Target filter query").isEqualTo(tfqList.getTotalElements());
assertEquals("Returns correct target filter query", tfq.getId(), tfqList.iterator().next().getId());
tfq2.setAutoAssignDistributionSet(distributionSet);
targetFilterQueryManagement.updateTargetFilterQuery(tfq2);
// check if find works for two
tfqList = targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(new PageRequest(0, 500),
distributionSet);
assertThat(2L).as("Target filter query count").isEqualTo(tfqList.getTotalElements());
Iterator<TargetFilterQuery> iterator = tfqList.iterator();
assertEquals("Returns correct target filter query 1", tfq.getId(), iterator.next().getId());
assertEquals("Returns correct target filter query 2", tfq2.getId(), iterator.next().getId());
// check if find works with name filter
tfqList = targetFilterQueryManagement.findTargetFilterQueryByAutoAssignDS(new PageRequest(0, 500),
distributionSet, "name==" + filterName);
assertThat(1L).as("Target filter query count").isEqualTo(tfqList.getTotalElements());
assertEquals("Returns correct target filter query", tfq2.getId(), tfqList.iterator().next().getId());
// check if find works for all with auto assign DS
tfqList = targetFilterQueryManagement.findTargetFilterQueryWithAutoAssignDS(new PageRequest(0, 500));
assertThat(2L).as("Target filter query count").isEqualTo(tfqList.getTotalElements());
iterator = tfqList.iterator();
assertEquals("Returns correct target filter query 1", tfq.getId(), iterator.next().getId());
assertEquals("Returns correct target filter query 2", tfq2.getId(), iterator.next().getId());
}
}

View File

@@ -1,85 +0,0 @@
/**
* 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;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.junit.Test;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
/**
* Test class for {@link TargetFilterQueryManagement}.
*
*/
@Features("Component Tests - Repository")
@Stories("Target Filter Query Management")
public class TargetFilterQueryManagenmentTest extends AbstractJpaIntegrationTest {
@Test
@Description("Test creation of target filter query.")
public void createTargetFilterQuery() {
final String filterName = "new target filter";
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
assertEquals("Retrieved newly created custom target filter", targetFilterQuery,
targetFilterQueryManagement.findTargetFilterQueryByName(filterName));
}
@Test
@Description("Checks if the EntityAlreadyExistsException is thrown if a targetfilterquery with the same name are created more than once.")
public void createDuplicateTargetFilterQuery() {
final String filterName = "new target filter duplicate";
targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
try {
targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
fail("should not have worked as query already exists");
} catch (final EntityAlreadyExistsException e) {
}
}
@Test
@Description("Test deletion of target filter query.")
public void deleteTargetFilterQuery() {
final String filterName = "delete_target_filter_query";
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
targetFilterQueryManagement.deleteTargetFilterQuery(targetFilterQuery.getId());
assertEquals("Returns null as the target filter is deleted", null,
targetFilterQueryManagement.findTargetFilterQueryById(targetFilterQuery.getId()));
}
@Test
@Description("Test updation of target filter query.")
public void updateTargetFilterQuery() {
final String filterName = "target_filter_01";
final TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery(filterName, "name==PendingTargets001"));
final String newQuery = "status==UNKNOWN";
targetFilterQuery.setQuery(newQuery);
targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery);
assertEquals("Returns updated target filter query", newQuery,
targetFilterQueryManagement.findTargetFilterQueryByName(filterName).getQuery());
}
}

View File

@@ -24,15 +24,8 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
import org.eclipse.hawkbit.repository.model.Action;
import org.eclipse.hawkbit.repository.model.*;
import org.eclipse.hawkbit.repository.model.Action.Status;
import org.eclipse.hawkbit.repository.model.ActionStatus;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetIdName;
import org.eclipse.hawkbit.repository.model.TargetTag;
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
import org.junit.Test;
import org.springframework.data.domain.Slice;
@@ -835,6 +828,26 @@ public class TargetManagementSearchTest extends AbstractJpaIntegrationTest {
}
@Test
@Description("Verifies that targets without given assigned DS are returned from repository.")
public void findTargetWithoutAssignedDistributionSet() {
final DistributionSet assignedSet = testdataFactory.createDistributionSet("");
final TargetFilterQuery tfq = targetFilterQueryManagement
.createTargetFilterQuery(entityFactory.generateTargetFilterQuery("tfq", "name==*"));
List<Target> unassignedTargets = targetManagement
.createTargets(testdataFactory.generateTargets(12, "unassigned"));
List<Target> assignedTargets = targetManagement.createTargets(testdataFactory.generateTargets(10, "assigned"));
deploymentManagement.assignDistributionSet(assignedSet, assignedTargets);
List<Target> result = targetManagement.findAllTargetsByTargetFilterQueryAndNonDS(pageReq,
assignedSet.getId(), tfq).getContent();
assertThat(result)
.as("count of targets").hasSize(unassignedTargets.size())
.as("contains all targets").containsAll(unassignedTargets);
}
@Test
@Description("Verfies that targets with given installed DS are returned from repository.")
public void findTargetByInstalledDistributionSet() {

View File

@@ -0,0 +1,154 @@
/**
* 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 java.util.List;
import java.util.stream.Collectors;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
import org.eclipse.hawkbit.repository.model.DistributionSet;
import org.eclipse.hawkbit.repository.model.Target;
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Slice;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Step;
import ru.yandex.qatools.allure.annotations.Stories;
import static org.fest.assertions.api.Assertions.assertThat;
/**
* Test class for {@link AutoAssignChecker}.
*
*/
@Features("Component Tests - Repository")
@Stories("Auto assign checker")
public class AutoAssignCheckerTest extends AbstractJpaIntegrationTest {
@Autowired
private AutoAssignChecker autoAssignChecker;
@Test
@Description("Test auto assignment of a DS to filtered targets")
public 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
TargetFilterQuery targetFilterQuery = targetFilterQueryManagement
.createTargetFilterQuery(new JpaTargetFilterQuery("filterA", "name==*"));
targetFilterQuery.setAutoAssignDistributionSet(setA);
targetFilterQueryManagement.updateTargetFilterQuery(targetFilterQuery);
final String targetDsAIdPref = "targ";
List<Target> targets = targetManagement.createTargets(
testdataFactory.generateTargets(100, targetDsAIdPref, targetDsAIdPref.concat(" description")));
int targetsCount = targets.size();
// assign set A to first 10 targets
deploymentManagement.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
deploymentManagement.assignDistributionSet(setB, targets.subList(0, 5));
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount);
// assign set B to next 10 targets
deploymentManagement.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.countTargetsByTargetFilterQueryAndNonDS(setA.getId(), targetFilterQuery))
.isEqualTo(90);
// Run the check
autoAssignChecker.check();
verifyThatTargetsHaveDistributionSetAssignment(setA, targets.subList(5, 100), targetsCount);
// first 5 should keep their dsB, because they already had the dsA once
verifyThatTargetsHaveDistributionSetAssignment(setB, targets.subList(0, 5), targetsCount);
}
@Test
@Description("Test auto assignment of an incomplete DS to filtered targets, that causes failures")
public void checkAutoAssignWithFailures() {
// incomplete distribution set that will be assigned
final DistributionSet setF = distributionSetManagement
.createDistributionSet(entityFactory.generateDistributionSet("dsA", "1", "incomplete ds",
testdataFactory.findOrCreateDefaultTestDsType(), null));
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
targetFilterQueryManagement.createTargetFilterQuery(
new JpaTargetFilterQuery("filterA", "id==" + targetDsFIdPref + "*", (JpaDistributionSet) setF));
// target filter query that matches failed bunch of targets
targetFilterQueryManagement.createTargetFilterQuery(
new JpaTargetFilterQuery("filterB", "id==" + targetDsAIdPref + "*", (JpaDistributionSet) setA));
List<Target> targetsF = targetManagement.createTargets(
testdataFactory.generateTargets(10, targetDsFIdPref, targetDsFIdPref.concat(" description")));
List<Target> targetsA = targetManagement.createTargets(
testdataFactory.generateTargets(10, targetDsAIdPref, targetDsAIdPref.concat(" description")));
int targetsCount = targetsA.size() + targetsF.size();
// assign set B to first 5 targets of fail group
deploymentManagement.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);
}
/**
* @param set the expected distribution set
* @param targets the targets that should have it
*/
@Step
private void verifyThatTargetsHaveDistributionSetAssignment(final DistributionSet set, List<Target> targets, int count) {
List<Long> targetIds = targets.stream().map(Target::getId).collect(Collectors.toList());
Slice<Target> targetsAll = targetManagement.findTargetsAll(new PageRequest(0, 1000));
assertThat(targetsAll).as("Count of targets").hasSize(count);
for (Target target : targetsAll) {
if(targetIds.contains(target.getId())) {
assertThat(target.getAssignedDistributionSet()).as("assigned DS").isEqualTo(set);
}
}
}
}

View File

@@ -0,0 +1,106 @@
/**
* 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.specifications;
import org.junit.Test;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.domain.Specifications;
import ru.yandex.qatools.allure.annotations.Description;
import ru.yandex.qatools.allure.annotations.Features;
import ru.yandex.qatools.allure.annotations.Stories;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@Features("Unit Tests - Repository")
@Stories("Specifications builder")
public class SpecificationsBuilderTest {
@Test
@Description("Test the combination of specs on an empty list which returns null")
public void combineWithAndEmptyList() {
List<Specification<Object>> specList = Collections.emptyList();
assertThat(SpecificationsBuilder.combineWithAnd(specList)).isNull();
}
@Test
@Description("Test the combination of specs on an immutable list with one entry")
public void combineWithAndSingleImmutableList() {
Specification<Object> spec = (root, query, cb) -> cb.equal(root.get("field1"), "testValue");
List<Specification<Object>> specList = Collections.singletonList(spec);
Specifications<Object> specifications = SpecificationsBuilder.combineWithAnd(specList);
assertThat(specifications).as("Specifications").isNotNull();
// mocks to call toPredicate on specifications
CriteriaBuilder criteriaBuilder = mock(CriteriaBuilder.class);
final Path field1 = mock(Path.class);
final Predicate equalPredicate = mock(Predicate.class);
final CriteriaQuery<Object[]> query = mock(CriteriaQuery.class);
final Root<Object> root = mock(Root.class);
when(criteriaBuilder.equal(any(Expression.class), anyString())).thenReturn(equalPredicate);
when(root.get("field1")).thenReturn(field1);
Predicate predicate = specifications.toPredicate(root, query, criteriaBuilder);
assertThat(predicate).isEqualTo(equalPredicate);
}
@Test
@Description("Test the combination of specs on a list with multiple entries")
public void combineWithAndList() {
Specification<Object> spec1 = (root, query, cb) -> cb.equal(root.get("field1"), "testValue1");
Specification<Object> spec2 = (root, query, cb) -> cb.equal(root.get("field2"), "testValue2");
List<Specification<Object>> specList = new ArrayList<>(2);
specList.add(spec1);
specList.add(spec2);
Specifications<Object> specifications = SpecificationsBuilder.combineWithAnd(specList);
assertThat(specifications).as("Specifications").isNotNull();
// mocks to call toPredicate on specifications
CriteriaBuilder criteriaBuilder = mock(CriteriaBuilder.class);
final Path field1 = mock(Path.class);
final Path field2 = mock(Path.class);
final Predicate equalPredicate1 = mock(Predicate.class);
final Predicate equalPredicate2 = mock(Predicate.class);
final Predicate combinedPredicate = mock(Predicate.class);
final CriteriaQuery<Object[]> query = mock(CriteriaQuery.class);
final Root<Object> root = mock(Root.class);
when(criteriaBuilder.equal(any(Path.class), eq("testValue1"))).thenReturn(equalPredicate1);
when(criteriaBuilder.equal(any(Path.class), eq("testValue2"))).thenReturn(equalPredicate2);
when(criteriaBuilder.and(eq(equalPredicate1), eq(equalPredicate2))).thenReturn(combinedPredicate);
when(root.get("field1")).thenReturn(field1);
when(root.get("field2")).thenReturn(field2);
Predicate predicate = specifications.toPredicate(root, query, criteriaBuilder);
assertThat(predicate).as("Combined predicate").isEqualTo(combinedPredicate);
}
}