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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user