* TenantAwareCacheManager define CacheEvictEvent which could be used to evict entities in general way * JpaTenantConfigurationManagement start using genera cache approach Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -36,7 +36,7 @@ public interface TenantConfigurationManagement extends PermissionSupport {
|
||||
/**
|
||||
* Adds or updates a specific configuration for a specific tenant.
|
||||
*
|
||||
* @param configurationKeyName the key of the configuration
|
||||
* @param keyName the key of the configuration
|
||||
* @param value the configuration value which will be written into the database.
|
||||
* @return the configuration value which was just written into the database.
|
||||
* @throws TenantConfigurationValidatorException if the {@code propertyType} and the value in general does not match the expected type and
|
||||
@@ -44,7 +44,7 @@ public interface TenantConfigurationManagement extends PermissionSupport {
|
||||
* @throws ConversionFailedException if the property cannot be converted to the given
|
||||
*/
|
||||
@PreAuthorize(value = SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
||||
<T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration(String configurationKeyName, T value);
|
||||
<T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration(String keyName, T value);
|
||||
|
||||
/**
|
||||
* Adds or updates a specific configuration for a specific tenant.
|
||||
@@ -58,6 +58,36 @@ public interface TenantConfigurationManagement extends PermissionSupport {
|
||||
@PreAuthorize(value = SpringEvalExpressions.HAS_UPDATE_REPOSITORY)
|
||||
<T extends Serializable> Map<String, TenantConfigurationValue<T>> addOrUpdateConfiguration(Map<String, T> configurations);
|
||||
|
||||
/**
|
||||
* Retrieves a configuration value from the e.g. tenant overwritten configuration values or in case the tenant does not a have a specific
|
||||
* configuration the global default value hold in the {@link Environment}.
|
||||
*
|
||||
* @param keyName the key of the configuration
|
||||
* @return the converted configuration value either from the tenant specific configuration stored or from the fallback default values or
|
||||
* {@code null} in case key has not been configured and not default value exists
|
||||
* @throws TenantConfigurationValidatorException if the {@code propertyType} and the value in general does not
|
||||
* match the expected type and format defined by the Key
|
||||
* @throws ConversionFailedException if the property cannot be converted to the given {@code propertyType}
|
||||
*/
|
||||
@PreAuthorize(value = SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
<T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(String keyName);
|
||||
|
||||
/**
|
||||
* Retrieves a configuration value from the e.g. tenant overwritten configuration values or in case the tenant does not a have a specific
|
||||
* configuration the global default value hold in the {@link Environment}.
|
||||
*
|
||||
* @param <T> the type of the configuration value
|
||||
* @param keyName the key of the configuration
|
||||
* @param propertyType the type of the configuration value, e.g. {@code String.class}, {@code Integer.class}, etc
|
||||
* @return the converted configuration value either from the tenant specific configuration stored or from the fallback default values or
|
||||
* {@code null} in case key has not been configured and not default value exists
|
||||
* @throws TenantConfigurationValidatorException if the {@code propertyType} and the value in general does not
|
||||
* match the expected type and format defined by the Key
|
||||
* @throws ConversionFailedException if the property cannot be converted to the given {@code propertyType}
|
||||
*/
|
||||
@PreAuthorize(value = SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
<T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(String keyName, Class<T> propertyType);
|
||||
|
||||
/**
|
||||
* Deletes a specific configuration for the current tenant. Does nothing in case there is no tenant specific configuration value.
|
||||
*
|
||||
@@ -66,51 +96,6 @@ public interface TenantConfigurationManagement extends PermissionSupport {
|
||||
@PreAuthorize(value = SpringEvalExpressions.HAS_DELETE_REPOSITORY)
|
||||
void deleteConfiguration(String configurationKey);
|
||||
|
||||
/**
|
||||
* Retrieves a configuration value from the e.g. tenant overwritten configuration values or in case the tenant does not a have a specific
|
||||
* configuration the global default value hold in the {@link Environment}.
|
||||
*
|
||||
* @param configurationKeyName the key of the configuration
|
||||
* @return the converted configuration value either from the tenant specific configuration stored or from the fallback default values or
|
||||
* {@code null} in case key has not been configured and not default value exists
|
||||
* @throws TenantConfigurationValidatorException if the {@code propertyType} and the value in general does not
|
||||
* match the expected type and format defined by the Key
|
||||
* @throws ConversionFailedException if the property cannot be converted to the given {@code propertyType}
|
||||
*/
|
||||
@PreAuthorize(value = SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
<T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(String configurationKeyName);
|
||||
|
||||
/**
|
||||
* Retrieves a configuration value from the e.g. tenant overwritten configuration values or in case the tenant does not a have a specific
|
||||
* configuration the global default value hold in the {@link Environment}.
|
||||
*
|
||||
* @param <T> the type of the configuration value
|
||||
* @param configurationKeyName the key of the configuration
|
||||
* @param propertyType the type of the configuration value, e.g. {@code String.class}, {@code Integer.class}, etc
|
||||
* @return the converted configuration value either from the tenant specific configuration stored or from the fallback default values or
|
||||
* {@code null} in case key has not been configured and not default value exists
|
||||
* @throws TenantConfigurationValidatorException if the {@code propertyType} and the value in general does not
|
||||
* match the expected type and format defined by the Key
|
||||
* @throws ConversionFailedException if the property cannot be converted to the given {@code propertyType}
|
||||
*/
|
||||
@PreAuthorize(value = SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
<T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(String configurationKeyName,
|
||||
Class<T> propertyType);
|
||||
|
||||
/**
|
||||
* returns the global configuration property either defined in the property file or a default value otherwise.
|
||||
*
|
||||
* @param <T> the type of the configuration value
|
||||
* @param configurationKeyName the key of the configuration
|
||||
* @param propertyType the type of the configuration value, e.g. {@code String.class}, {@code Integer.class}, etc
|
||||
* @return the global configured value
|
||||
* @throws TenantConfigurationValidatorException if the {@code propertyType} and the value in the property file or the default value
|
||||
* does not match the expected type and format defined by the Key
|
||||
* @throws ConversionFailedException if the property cannot be converted to the given {@code propertyType}
|
||||
*/
|
||||
@PreAuthorize(value = SpringEvalExpressions.HAS_READ_REPOSITORY)
|
||||
<T> T getGlobalConfigurationValue(String configurationKeyName, Class<T> propertyType);
|
||||
|
||||
@PreAuthorize(value = "hasAuthority('READ_" + SpPermission.TARGET + "')")
|
||||
Function<Target, PollStatus> pollStatusResolver();
|
||||
}
|
||||
@@ -20,6 +20,10 @@ import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
|
||||
/**
|
||||
* A base definition class for an event which contains an id.
|
||||
* <p/>
|
||||
*
|
||||
* Note: it implements {@link org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent} methods but in order
|
||||
* to be really include in the cache eviction process the subclasses must declare that it implements that interface.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PROTECTED)
|
||||
@Getter
|
||||
@@ -38,4 +42,13 @@ public abstract class RemoteIdEvent extends RemoteTenantAwareEvent {
|
||||
this.entityClass = entityClass.getName();
|
||||
this.entityId = entityId;
|
||||
}
|
||||
|
||||
public String getCacheName() {
|
||||
final int index = entityClass.lastIndexOf('.');
|
||||
return index < 0 ? entityClass : entityClass.substring(index + 1);
|
||||
}
|
||||
|
||||
public Object getCacheKey() {
|
||||
return entityId;
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
|
||||
/**
|
||||
* Defines the remote event of deleting a {@link org.eclipse.hawkbit.repository.model.TenantConfiguration}.
|
||||
@@ -25,7 +27,7 @@ import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
@Getter
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class TenantConfigurationDeletedEvent extends RemoteIdEvent implements EntityDeletedEvent {
|
||||
public class TenantConfigurationDeletedEvent extends RemoteIdEvent implements EntityDeletedEvent, CacheEvictEvent {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -41,4 +43,10 @@ public class TenantConfigurationDeletedEvent extends RemoteIdEvent implements En
|
||||
this.configKey = configKey;
|
||||
this.configValue = configValue;
|
||||
}
|
||||
|
||||
// overrides since the default impl is based on entity id while the tenant cache is key (string) based
|
||||
@Override
|
||||
public String getCacheKey() {
|
||||
return configKey;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,4 +27,10 @@ public class TenantConfigurationCreatedEvent extends RemoteEntityEvent<TenantCon
|
||||
public TenantConfigurationCreatedEvent(final TenantConfiguration tenantConfiguration) {
|
||||
super(tenantConfiguration);
|
||||
}
|
||||
|
||||
// overrides since the default impl is based on entity id while the tenant cache is key (string) based
|
||||
@Override
|
||||
public String getCacheKey() {
|
||||
return getEntity().map(TenantConfiguration::getKey).orElse(null); // null will clean all tenant cache
|
||||
}
|
||||
}
|
||||
@@ -14,12 +14,13 @@ import java.io.Serial;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
|
||||
/**
|
||||
* Defines the remote event of updating a {@link TenantConfiguration}.
|
||||
*/
|
||||
@NoArgsConstructor // for serialization libs like jackson
|
||||
public class TenantConfigurationUpdatedEvent extends RemoteEntityEvent<TenantConfiguration> implements EntityUpdatedEvent {
|
||||
public class TenantConfigurationUpdatedEvent extends RemoteEntityEvent<TenantConfiguration> implements EntityUpdatedEvent, CacheEvictEvent {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -27,4 +28,10 @@ public class TenantConfigurationUpdatedEvent extends RemoteEntityEvent<TenantCon
|
||||
public TenantConfigurationUpdatedEvent(final TenantConfiguration tenantConfiguration) {
|
||||
super(tenantConfiguration);
|
||||
}
|
||||
|
||||
// overrides since the default impl is based on entity id while the tenant cache is key (string) based
|
||||
@Override
|
||||
public String getCacheKey() {
|
||||
return getEntity().map(TenantConfiguration::getKey).orElse(null); // null will clean all tenant cache
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import java.util.Set;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.event.remote.AbstractRemoteEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent;
|
||||
@@ -107,12 +108,12 @@ public final class EventPublisherHolder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishEvent(final Object event) {
|
||||
public void publishEvent(@NonNull final Object event) {
|
||||
routeEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void publishEvent(final ApplicationEvent event) {
|
||||
public void publishEvent(@NonNull final ApplicationEvent event) {
|
||||
routeEvent(event);
|
||||
}
|
||||
|
||||
@@ -137,8 +138,8 @@ public final class EventPublisherHolder {
|
||||
log.debug("Publishing Service event: {} to remote channel: {}", serviceEvent, serviceEventChannel);
|
||||
streamBridge.send(serviceEventChannel, serviceEvent);
|
||||
} else {
|
||||
log.error("No Service event created for: {}. Skipping send Service event to Service channel. {}", remoteEvent.getClass(),
|
||||
serviceEventChannel);
|
||||
log.error("No Service event created for: {}. Skipping send Service event to Service channel. {}",
|
||||
remoteEvent.getClass(), serviceEventChannel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_DELAY;
|
||||
import static org.eclipse.hawkbit.repository.jpa.configuration.Constants.TX_RT_MAX;
|
||||
import static org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor.afterCommit;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -38,8 +39,8 @@ import jakarta.validation.constraints.NotNull;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.Identifiable;
|
||||
import org.eclipse.hawkbit.repository.RepositoryManagement;
|
||||
import org.eclipse.hawkbit.repository.QueryField;
|
||||
import org.eclipse.hawkbit.repository.RepositoryManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.jpa.Jpa;
|
||||
@@ -48,9 +49,10 @@ import org.eclipse.hawkbit.repository.jpa.JpaRepositoryConfiguration;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaBaseEntity_;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.BaseEntityRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.ql.QLSupport;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.BaseEntityRepository;
|
||||
import org.eclipse.hawkbit.utils.ObjectCopyUtil;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -132,6 +134,7 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
public T create(final C create) {
|
||||
getCache().ifPresent(cache -> afterCommit(cache::clear));
|
||||
return jpaRepository.save(AccessController.Operation.CREATE, jpaEntity(create));
|
||||
}
|
||||
|
||||
@@ -139,17 +142,47 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
public List<T> create(final Collection<C> create) {
|
||||
getCache().ifPresent(cache -> afterCommit(cache::clear));
|
||||
return jpaRepository.saveAll(AccessController.Operation.CREATE, create.stream().map(this::jpaEntity).toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T get(final long id) {
|
||||
return jpaRepository.getById(id);
|
||||
final Cache cache = getCache().orElse(null);
|
||||
if (cache == null) {
|
||||
return jpaRepository.getById(id);
|
||||
} else {
|
||||
final T cached = (T) cache.get(id);
|
||||
if (cached == null) {
|
||||
final T entity = jpaRepository.getById(id);
|
||||
if (entity != null) { // should not be null - but throw exception - but for sure check
|
||||
cache.put(id, entity);
|
||||
}
|
||||
return entity;
|
||||
} else {
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Optional<T> find(final long id) {
|
||||
return jpaRepository.findById(id);
|
||||
final Cache cache = getCache().orElse(null);
|
||||
if (cache == null) {
|
||||
return jpaRepository.findById(id);
|
||||
} else {
|
||||
final T cached = (T) cache.get(id);
|
||||
if (cached == null) {
|
||||
final Optional<T> entity = jpaRepository.findById(id);
|
||||
// should not be null - but throw exception - but for sure check
|
||||
entity.ifPresent(t -> cache.put(id, t));
|
||||
return entity;
|
||||
} else {
|
||||
return Optional.of(cached);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -193,6 +226,7 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
@SuppressWarnings("java:S1066") // javaS1066 - better readable that way
|
||||
public T update(final U update) {
|
||||
getCache().ifPresent(cache -> afterCommit(() -> cache.evict(update.getId())));
|
||||
final T entity = getValid(update.getId());
|
||||
// update getId has no setter in target JPA entity but shall have getter and the value shall be the same
|
||||
// otherwise the Utils will throw an exception that there is no counterpart setter for getId
|
||||
@@ -230,6 +264,7 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
if (toSave.isEmpty()) {
|
||||
return toUpdate;
|
||||
} else {
|
||||
getCache().ifPresent(cache -> afterCommit(() -> toSave.forEach(updated -> cache.evict(updated.getId()))));
|
||||
final List<T> savedEntities = jpaRepository.saveAll(toSave);
|
||||
final Map<Long, T> result = new HashMap<>(toUpdate);
|
||||
for (final T savedEntity : savedEntities) {
|
||||
@@ -243,6 +278,7 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
public void delete(final long id) {
|
||||
getCache().ifPresent(cache -> afterCommit(() -> cache.evict(id)));
|
||||
delete0(List.of(id));
|
||||
}
|
||||
|
||||
@@ -250,6 +286,7 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = TX_RT_MAX, backoff = @Backoff(delay = TX_RT_DELAY))
|
||||
public void delete(final Collection<Long> ids) {
|
||||
getCache().ifPresent(cache -> afterCommit(() -> ids.forEach(cache::evict)));
|
||||
delete0(ids);
|
||||
}
|
||||
|
||||
@@ -314,6 +351,11 @@ abstract class AbstractJpaRepositoryManagement<T extends AbstractJpaBaseEntity,
|
||||
}
|
||||
}
|
||||
|
||||
// if cache is supported for the entities - override this method
|
||||
protected Optional<Cache> getCache() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private List<T> findAllById(final Collection<Long> ids, final boolean throwIfNotFound) {
|
||||
final List<T> foundDs = jpaRepository.findAllById(ids);
|
||||
if (throwIfNotFound && foundDs.size() != ids.size()) {
|
||||
|
||||
@@ -16,10 +16,10 @@ import jakarta.persistence.EntityManager;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.artifact.ArtifactStorage;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantStatsManagement;
|
||||
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.jpa.CurrentTenantCacheKeyGenerator;
|
||||
import org.eclipse.hawkbit.repository.jpa.SystemManagementCacheKeyGenerator;
|
||||
@@ -47,6 +47,7 @@ import org.eclipse.hawkbit.repository.model.report.SystemUsageReport;
|
||||
import org.eclipse.hawkbit.repository.model.report.SystemUsageReportWithTenants;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
|
||||
@@ -160,7 +161,6 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
}
|
||||
|
||||
final String tenant = t.toUpperCase();
|
||||
TenantAwareCacheManager.getInstance().evictTenant(tenant);
|
||||
tenantAware.runAsTenant(tenant, () -> DeploymentHelper.runInNewTransaction(txManager, "deleteTenant", status -> {
|
||||
tenantMetaDataRepository.deleteByTenantIgnoreCase(tenant);
|
||||
tenantConfigurationRepository.deleteByTenant(tenant);
|
||||
@@ -177,6 +177,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
softwareModuleTypeRepository.deleteByTenant(tenant);
|
||||
return null;
|
||||
}));
|
||||
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new CacheEvictEvent.Default(tenant, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_GATEWAY_SECURITY_TOKEN;
|
||||
import static org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor.afterCommit;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
|
||||
@@ -26,14 +25,13 @@ import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.TargetFields;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TenantConfigurationDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.RemoteEntityEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValidatorException;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValueChangeNotAllowedException;
|
||||
@@ -51,14 +49,11 @@ import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.PollingTime;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.PollingTime.PollingInterval;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
@@ -79,7 +74,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "tenant-configuration-management" }, matchIfMissing = true)
|
||||
public class JpaTenantConfigurationManagement implements TenantConfigurationManagement {
|
||||
|
||||
private static final String CACHE_TENANT_CONFIGURATION_NAME = "tenantConfiguration";
|
||||
private static final String CACHE_TENANT_CONFIGURATION_NAME = JpaTenantConfiguration.class.getSimpleName();
|
||||
private static final ConfigurableConversionService CONVERSION_SERVICE = new DefaultConversionService();
|
||||
|
||||
private final TenantConfigurationRepository tenantConfigurationRepository;
|
||||
@@ -96,12 +91,11 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(value = CACHE_TENANT_CONFIGURATION_NAME, key = "#configurationKeyName")
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public <T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration(final String configurationKeyName, final T value) {
|
||||
return addOrUpdateConfiguration0(Map.of(configurationKeyName, value)).values().iterator().next();
|
||||
public <T extends Serializable> TenantConfigurationValue<T> addOrUpdateConfiguration(final String keyName, final T value) {
|
||||
return addOrUpdateConfiguration0(Map.of(keyName, value)).values().iterator().next();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,89 +103,38 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public <T extends Serializable> Map<String, TenantConfigurationValue<T>> addOrUpdateConfiguration(final Map<String, T> configurations) {
|
||||
// Register a callback to be invoked after the transaction is committed - for cache eviction
|
||||
afterCommit(() -> {
|
||||
final Cache cache = TenantAwareCacheManager.getInstance().getCache(CACHE_TENANT_CONFIGURATION_NAME);
|
||||
if (cache != null) {
|
||||
configurations.keySet().forEach(cache::evict);
|
||||
}
|
||||
});
|
||||
|
||||
return addOrUpdateConfiguration0(configurations);
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict(value = CACHE_TENANT_CONFIGURATION_NAME, key = "#configurationKeyName")
|
||||
public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(final String keyName) {
|
||||
return getConfigurationValue0(keyName, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(final String keyName, final Class<T> propertyType) {
|
||||
return getConfigurationValue0(keyName, propertyType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void deleteConfiguration(final String configurationKeyName) {
|
||||
tenantConfigurationRepository.deleteByKey(configurationKeyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
// TODO - check if cache works
|
||||
// @Cacheable(value = CACHE_TENANT_CONFIGURATION_NAME, key = "#configurationKeyName")
|
||||
public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(final String configurationKeyName) {
|
||||
checkAccess(configurationKeyName);
|
||||
|
||||
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName);
|
||||
|
||||
return getConfigurationValue(configurationKeyName, (Class<T>) configurationKey.getDataType());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = CACHE_TENANT_CONFIGURATION_NAME, key = "#configurationKeyName")
|
||||
public <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue(
|
||||
final String configurationKeyName, final Class<T> propertyType) {
|
||||
checkAccess(configurationKeyName);
|
||||
|
||||
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName);
|
||||
validateTenantConfigurationDataType(configurationKey, propertyType);
|
||||
|
||||
final TenantConfiguration tenantConfiguration = tenantConfigurationRepository.findByKey(configurationKey.getKeyName());
|
||||
return buildTenantConfigurationValueByKey(configurationKey, propertyType, tenantConfiguration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getGlobalConfigurationValue(final String configurationKeyName, final Class<T> propertyType) {
|
||||
checkAccess(configurationKeyName);
|
||||
|
||||
final TenantConfigurationKey key = tenantConfigurationProperties.fromKeyName(configurationKeyName);
|
||||
if (!key.getDataType().isAssignableFrom(propertyType)) {
|
||||
throw new TenantConfigurationValidatorException(
|
||||
String.format("Cannot parse the database value of type %s into the type %s.", key.getDataType(), propertyType));
|
||||
}
|
||||
|
||||
return CONVERSION_SERVICE.convert(key.getDefaultValue(), propertyType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that cache eviction takes place in microservice mode in case of deletions.
|
||||
*
|
||||
* @param event The event indicating that a configuration value has been deleted.
|
||||
*/
|
||||
@EventListener
|
||||
public void onTenantConfigurationDeletedEvent(final TenantConfigurationDeletedEvent event) {
|
||||
evictCacheEntryByKeyIfPresent(event.getConfigKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that cache eviction takes place in microservice mode in case of creation or update events.
|
||||
*
|
||||
* @param event The event indicating that a configuration value has been created or updated.
|
||||
*/
|
||||
@EventListener
|
||||
public void onTenantConfigurationRemoteEntityEvent(final RemoteEntityEvent<TenantConfiguration> event) {
|
||||
event.getEntity().ifPresent(tenantConfiguration -> evictCacheEntryByKeyIfPresent(tenantConfiguration.getKey()));
|
||||
public void deleteConfiguration(final String keyName) {
|
||||
tenantConfigurationRepository.deleteByKey(keyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("java:S3776") // java:S3776 - not really too complex
|
||||
public Function<Target, PollStatus> pollStatusResolver() {
|
||||
final PollingTime pollingTime = new PollingTime(
|
||||
getConfigurationValue(TenantConfigurationKey.POLLING_TIME, String.class).getValue());
|
||||
Objects.requireNonNull(getConfigurationValue0(TenantConfigurationKey.POLLING_TIME, String.class),
|
||||
"Polling time shall always be non-null")
|
||||
.getValue());
|
||||
final Duration pollingOverdueTime = DurationHelper.fromString(
|
||||
getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME, String.class).getValue());
|
||||
Objects.requireNonNull(getConfigurationValue0(TenantConfigurationKey.POLLING_OVERDUE_TIME, String.class),
|
||||
"Polling overdue time shall always be non-null")
|
||||
.getValue());
|
||||
return target -> {
|
||||
final Long lastTargetQuery = target.getLastTargetQuery();
|
||||
if (lastTargetQuery == null) {
|
||||
@@ -214,35 +157,18 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
};
|
||||
}
|
||||
|
||||
private static PollStatus pollStatus(
|
||||
final long lastTargetQuery,
|
||||
final PollingTime.PollingInterval pollingInterval, final Duration pollingOverdueTime) {
|
||||
final LocalDateTime currentDate = LocalDateTime.now();
|
||||
final LocalDateTime lastPollDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastTargetQuery), ZoneId.systemDefault());
|
||||
LocalDateTime nextPollDate = lastPollDate.plus(pollingInterval.getInterval());
|
||||
if (pollingInterval.getDeviationPercent() > 0) {
|
||||
nextPollDate = nextPollDate.plus(
|
||||
pollingInterval.getInterval().toMillis() * pollingInterval.getDeviationPercent() / 100,
|
||||
ChronoUnit.MILLIS);
|
||||
}
|
||||
final LocalDateTime overdueDate = nextPollDate.plus(pollingOverdueTime);
|
||||
return new PollStatus(lastPollDate, nextPollDate, overdueDate, currentDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the data type of the tenant configuration. If it is possible to cast to the given data type.
|
||||
*/
|
||||
private static <T> void validateTenantConfigurationDataType(final TenantConfigurationKey configurationKey, final Class<T> propertyType) {
|
||||
if (!configurationKey.getDataType().isAssignableFrom(propertyType)) {
|
||||
throw new TenantConfigurationValidatorException(
|
||||
String.format(
|
||||
"Cannot parse the database value of type %s into the type %s.",
|
||||
configurationKey.getDataType(), propertyType));
|
||||
throw new TenantConfigurationValidatorException(String.format(
|
||||
"Cannot parse the database value of type %s into the type %s.", configurationKey.getDataType(), propertyType));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAccess(final String configurationKeyName) {
|
||||
if (AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY.equalsIgnoreCase(configurationKeyName)) {
|
||||
private void checkAccess(final String keyName) {
|
||||
if (AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY.equalsIgnoreCase(keyName)) {
|
||||
final SystemSecurityContext systemSecurityContext = SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
|
||||
if (!SystemSecurityContext.isCurrentThreadSystemCode() && !systemSecurityContext.hasPermission(READ_GATEWAY_SECURITY_TOKEN)) {
|
||||
throw new InsufficientPermissionException(
|
||||
@@ -251,14 +177,15 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends Serializable> Map<String, TenantConfigurationValue<T>> addOrUpdateConfiguration0(final Map<String, T> configurations) {
|
||||
final List<JpaTenantConfiguration> configurationList = new ArrayList<>();
|
||||
configurations.forEach((configurationKeyName, value) -> {
|
||||
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName);
|
||||
configurations.forEach((keyName, value) -> {
|
||||
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(keyName);
|
||||
if (!configurationKey.getDataType().isAssignableFrom(value.getClass())) {
|
||||
throw new TenantConfigurationValidatorException(String.format(
|
||||
"Cannot parse the value %s of type %s into the type %s defined by the configuration key.", value,
|
||||
value.getClass(), configurationKey.getDataType()));
|
||||
"Cannot parse the value %s of type %s into the type %s defined by the configuration key.",
|
||||
value, value.getClass(), configurationKey.getDataType()));
|
||||
}
|
||||
configurationKey.validate(value, applicationContext);
|
||||
// additional validation for specific configuration keys
|
||||
@@ -279,43 +206,72 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
tenantConfiguration.setValue(value.toString());
|
||||
}
|
||||
|
||||
assertValueChangeIsAllowed(configurationKeyName, tenantConfiguration);
|
||||
assertValueChangeIsAllowed(keyName, tenantConfiguration);
|
||||
configurationList.add(tenantConfiguration);
|
||||
});
|
||||
return tenantConfigurationRepository.saveAll(configurationList).
|
||||
stream().
|
||||
collect(Collectors.toMap(
|
||||
JpaTenantConfiguration::getKey,
|
||||
updatedTenantConfiguration -> TenantConfigurationValue.<T> builder()
|
||||
.global(false)
|
||||
.createdBy(updatedTenantConfiguration.getCreatedBy())
|
||||
.createdAt(updatedTenantConfiguration.getCreatedAt())
|
||||
.lastModifiedAt(updatedTenantConfiguration.getLastModifiedAt())
|
||||
.lastModifiedBy(updatedTenantConfiguration.getLastModifiedBy())
|
||||
.value(CONVERSION_SERVICE.convert(
|
||||
updatedTenantConfiguration.getValue(),
|
||||
(Class<T>) configurations.get(updatedTenantConfiguration.getKey()).getClass()))
|
||||
.build()));
|
||||
}
|
||||
|
||||
final List<JpaTenantConfiguration> jpaTenantConfigurations = tenantConfigurationRepository.saveAll(configurationList);
|
||||
return jpaTenantConfigurations.stream().collect(Collectors.toMap(
|
||||
JpaTenantConfiguration::getKey,
|
||||
updatedTenantConfiguration -> {
|
||||
@SuppressWarnings("unchecked") final Class<T> clazzT = (Class<T>) configurations.get(updatedTenantConfiguration.getKey())
|
||||
.getClass();
|
||||
return TenantConfigurationValue.<T> builder().global(false)
|
||||
.createdBy(updatedTenantConfiguration.getCreatedBy())
|
||||
.createdAt(updatedTenantConfiguration.getCreatedAt())
|
||||
.lastModifiedAt(updatedTenantConfiguration.getLastModifiedAt())
|
||||
.lastModifiedBy(updatedTenantConfiguration.getLastModifiedBy())
|
||||
.value(CONVERSION_SERVICE.convert(updatedTenantConfiguration.getValue(), clazzT))
|
||||
.build();
|
||||
}));
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue0(final String keyName, final Class<T> propertyType) {
|
||||
checkAccess(keyName);
|
||||
|
||||
final TenantConfigurationKey key = tenantConfigurationProperties.fromKeyName(keyName);
|
||||
if (propertyType != null) {
|
||||
validateTenantConfigurationDataType(key, propertyType);
|
||||
}
|
||||
|
||||
final TenantConfiguration tenantConfiguration = TenantAwareCacheManager.getInstance().getCache(CACHE_TENANT_CONFIGURATION_NAME)
|
||||
.get(key.getKeyName(), () -> tenantConfigurationRepository.findByKey(key.getKeyName()));
|
||||
return buildTenantConfigurationValueByKey(key, propertyType == null ? (Class<T>) key.getDataType() : propertyType, tenantConfiguration);
|
||||
}
|
||||
|
||||
private <T extends Serializable> TenantConfigurationValue<T> buildTenantConfigurationValueByKey(
|
||||
final TenantConfigurationKey configurationKey, final Class<T> propertyType, final TenantConfiguration tenantConfiguration) {
|
||||
if (tenantConfiguration != null) {
|
||||
return TenantConfigurationValue.<T> builder().global(false).createdBy(tenantConfiguration.getCreatedBy())
|
||||
return TenantConfigurationValue.<T> builder().global(false)
|
||||
.createdBy(tenantConfiguration.getCreatedBy())
|
||||
.createdAt(tenantConfiguration.getCreatedAt())
|
||||
.lastModifiedAt(tenantConfiguration.getLastModifiedAt())
|
||||
.lastModifiedBy(tenantConfiguration.getLastModifiedBy())
|
||||
.value(CONVERSION_SERVICE.convert(tenantConfiguration.getValue(), propertyType)).build();
|
||||
} else if (configurationKey.getDefaultValue() != null) {
|
||||
return TenantConfigurationValue.<T> builder().global(true).createdBy(null).createdAt(null)
|
||||
.lastModifiedAt(null).lastModifiedBy(null)
|
||||
.value(getGlobalConfigurationValue(configurationKey.getKeyName(), propertyType)).build();
|
||||
return TenantConfigurationValue.<T> builder().global(true)
|
||||
.createdBy(null)
|
||||
.createdAt(null)
|
||||
.lastModifiedAt(null)
|
||||
.lastModifiedBy(null)
|
||||
.value(getGlobalConfigurationValue0(configurationKey.getKeyName(), propertyType)).build();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T getGlobalConfigurationValue0(final String keyName, final Class<T> propertyType) {
|
||||
checkAccess(keyName);
|
||||
|
||||
final TenantConfigurationKey key = tenantConfigurationProperties.fromKeyName(keyName);
|
||||
if (!key.getDataType().isAssignableFrom(propertyType)) {
|
||||
throw new TenantConfigurationValidatorException(
|
||||
String.format("Cannot parse the database value of type %s into the type %s.", key.getDataType(), propertyType));
|
||||
}
|
||||
|
||||
return CONVERSION_SERVICE.convert(key.getDefaultValue(), propertyType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the requested configuration value change is allowed. Throws a {@link TenantConfigurationValueChangeNotAllowedException}
|
||||
* otherwise.
|
||||
@@ -326,19 +282,10 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
*/
|
||||
private void assertValueChangeIsAllowed(final String key, final JpaTenantConfiguration valueChange) {
|
||||
assertMultiAssignmentsValueChange(key, valueChange);
|
||||
assertAutoCloseValueChange(key, valueChange);
|
||||
assertAutoCloseValueChange(key);
|
||||
assertBatchAssignmentValueChange(key, valueChange);
|
||||
}
|
||||
|
||||
@SuppressWarnings("squid:S1172")
|
||||
private void assertAutoCloseValueChange(final String key, final JpaTenantConfiguration valueChange) {
|
||||
if (REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED.equals(key)
|
||||
&& Boolean.TRUE.equals(getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue())) {
|
||||
log.debug("The property '{}' must not be changed because the Multi-Assignments feature is currently enabled.", key);
|
||||
throw new TenantConfigurationValueChangeNotAllowedException();
|
||||
}
|
||||
}
|
||||
|
||||
private void assertMultiAssignmentsValueChange(final String key, final JpaTenantConfiguration valueChange) {
|
||||
if (MULTI_ASSIGNMENTS_ENABLED.equals(key) && !Boolean.parseBoolean(valueChange.getValue())) {
|
||||
log.debug("The Multi-Assignments '{}' feature cannot be disabled.", key);
|
||||
@@ -355,6 +302,14 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
}
|
||||
}
|
||||
|
||||
private void assertAutoCloseValueChange(final String key) {
|
||||
if (REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED.equals(key)
|
||||
&& Boolean.TRUE.equals(getConfigurationValue0(MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue())) {
|
||||
log.debug("The property '{}' must not be changed because the Multi-Assignments feature is currently enabled.", key);
|
||||
throw new TenantConfigurationValueChangeNotAllowedException();
|
||||
}
|
||||
}
|
||||
|
||||
private void assertBatchAssignmentValueChange(final String key, final JpaTenantConfiguration valueChange) {
|
||||
if (BATCH_ASSIGNMENTS_ENABLED.equals(key) && Boolean.parseBoolean(valueChange.getValue())) {
|
||||
JpaTenantConfiguration multiConfig = tenantConfigurationRepository.findByKey(MULTI_ASSIGNMENTS_ENABLED);
|
||||
@@ -367,10 +322,15 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
}
|
||||
}
|
||||
|
||||
private void evictCacheEntryByKeyIfPresent(final String key) {
|
||||
final Cache cache = TenantAwareCacheManager.getInstance().getCache(CACHE_TENANT_CONFIGURATION_NAME);
|
||||
if (cache != null) {
|
||||
cache.evictIfPresent(key);
|
||||
private static PollStatus pollStatus(final long lastTargetQuery, final PollingInterval pollingInterval, final Duration pollingOverdueTime) {
|
||||
final LocalDateTime currentDate = LocalDateTime.now();
|
||||
final LocalDateTime lastPollDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(lastTargetQuery), ZoneId.systemDefault());
|
||||
LocalDateTime nextPollDate = lastPollDate.plus(pollingInterval.getInterval());
|
||||
if (pollingInterval.getDeviationPercent() > 0) {
|
||||
nextPollDate = nextPollDate.plus(
|
||||
pollingInterval.getInterval().toMillis() * pollingInterval.getDeviationPercent() / 100, ChronoUnit.MILLIS);
|
||||
}
|
||||
final LocalDateTime overdueDate = nextPollDate.plus(pollingOverdueTime);
|
||||
return new PollStatus(lastPollDate, nextPollDate, overdueDate, currentDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,6 @@ import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetExcepti
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
|
||||
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
|
||||
@@ -158,8 +157,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
verifyThrownExceptionBy(() -> deploymentManagement.countActionsByTarget(NOT_EXIST_ID), "Target");
|
||||
verifyThrownExceptionBy(() -> deploymentManagement.countActionsByTarget("xxx", NOT_EXIST_ID), "Target");
|
||||
|
||||
verifyThrownExceptionBy(() -> findActionsByDistributionSet(PAGE, NOT_EXIST_IDL),
|
||||
"DistributionSet");
|
||||
verifyThrownExceptionBy(() -> findActionsByDistributionSet(PAGE, NOT_EXIST_IDL), "DistributionSet");
|
||||
verifyThrownExceptionBy(() -> deploymentManagement.findActionsByTarget(NOT_EXIST_ID, PAGE), "Target");
|
||||
verifyThrownExceptionBy(() -> deploymentManagement.findActionsByTarget("id==*", NOT_EXIST_ID, PAGE), "Target");
|
||||
|
||||
@@ -169,20 +167,19 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* Test verifies that the repistory retrieves the action including all defined (lazy) details.
|
||||
* Test verifies that the repository retrieves the action including all defined (lazy) details.
|
||||
*/
|
||||
@Test
|
||||
void findActionWithLazyDetails() {
|
||||
final DistributionSet testDs = testdataFactory.createDistributionSet("TestDs", "1.0",
|
||||
new ArrayList<>());
|
||||
final DistributionSet testDs = testdataFactory.createDistributionSet("TestDs", "1.0", List.of());
|
||||
final List<Target> testTarget = testdataFactory.createTargets(1);
|
||||
// one action with one action status is generated
|
||||
final Long actionId = getFirstAssignedActionId(assignDistributionSet(testDs, testTarget));
|
||||
final Action action = deploymentManagement.findActionWithDetails(actionId).get();
|
||||
final Action action = deploymentManagement.findActionWithDetails(actionId).orElseThrow();
|
||||
|
||||
assertThat(action.getDistributionSet()).as("DistributionSet in action").isNotNull();
|
||||
assertThat(action.getTarget()).as("Target in action").isNotNull();
|
||||
assertThat(deploymentManagement.findAssignedDistributionSet(action.getTarget().getControllerId()).get())
|
||||
assertThat(deploymentManagement.findAssignedDistributionSet(action.getTarget().getControllerId()).orElseThrow())
|
||||
.as("AssignedDistributionSet of target in action")
|
||||
.isNotNull();
|
||||
}
|
||||
@@ -192,7 +189,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
*/
|
||||
@Test
|
||||
void findActionByTargetId() {
|
||||
final DistributionSet testDs = testdataFactory.createDistributionSet("TestDs", "1.0", new ArrayList<>());
|
||||
final DistributionSet testDs = testdataFactory.createDistributionSet("TestDs", "1.0", List.of());
|
||||
final List<Target> testTarget = testdataFactory.createTargets(1);
|
||||
// one action with one action status is generated
|
||||
final Long actionId = getFirstAssignedActionId(assignDistributionSet(testDs, testTarget));
|
||||
@@ -255,7 +252,6 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
// act
|
||||
final Page<ActionStatus> actionStates = deploymentManagement.findActionStatusByAction(actionId, PAGE);
|
||||
|
||||
assertThat(actionStates.getContent()).hasSize(1);
|
||||
assertThat(actionStates.getContent().get(0)).as("Action-status of action").isEqualTo(expectedActionStatus);
|
||||
}
|
||||
@@ -265,25 +261,24 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
*/
|
||||
@Test
|
||||
void findMessagesByActionStatusId() {
|
||||
final DistributionSet testDs = testdataFactory.createDistributionSet("TestDs", "1.0", new ArrayList<>());
|
||||
final DistributionSet testDs = testdataFactory.createDistributionSet("TestDs", "1.0", List.of());
|
||||
final List<Target> testTarget = testdataFactory.createTargets(1);
|
||||
// one action with one action status is generated
|
||||
final Long actionId = getFirstAssignedActionId(assignDistributionSet(testDs, testTarget));
|
||||
// create action-status entry with one message
|
||||
controllerManagement.addUpdateActionStatus(ActionStatusCreate.builder().actionId(actionId)
|
||||
.status(Action.Status.FINISHED).messages(List.of("finished message")).build());
|
||||
controllerManagement.addUpdateActionStatus(ActionStatusCreate.builder()
|
||||
.actionId(actionId).status(Action.Status.FINISHED).messages(List.of("finished message")).build());
|
||||
final Page<ActionStatus> actionStates = deploymentManagement.findActionStatusByAction(actionId, PAGE);
|
||||
|
||||
// find newly created action-status entry with message
|
||||
final JpaActionStatus actionStatusWithMessage = actionStates.getContent().stream()
|
||||
.map(JpaActionStatus.class::cast)
|
||||
.filter(entry -> entry.getMessages() != null && !entry.getMessages().isEmpty())
|
||||
.findFirst().get();
|
||||
.findFirst().orElseThrow();
|
||||
final String expectedMsg = actionStatusWithMessage.getMessages().get(0);
|
||||
|
||||
// act
|
||||
final Page<String> messages = deploymentManagement.findMessagesByActionStatusId(actionStatusWithMessage.getId(), PAGE);
|
||||
|
||||
assertThat(actionStates.getTotalElements()).as("Two action-states in total").isEqualTo(2L);
|
||||
assertThat(messages.getContent().get(0)).as("Message of action-status").isEqualTo(expectedMsg);
|
||||
}
|
||||
@@ -301,7 +296,6 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
assignDS.add(100_000L);
|
||||
|
||||
final Long tagId = distributionSetTagManagement.create(DistributionSetTagManagement.Create.builder().name("Tag1").build()).getId();
|
||||
|
||||
assertThatExceptionOfType(EntityNotFoundException.class)
|
||||
.isThrownBy(() -> distributionSetManagement.assignTag(assignDS, tagId))
|
||||
.withMessageContaining("DistributionSet")
|
||||
@@ -363,7 +357,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
// we cancel second -> back to first
|
||||
deploymentManagement.cancelAction(secondAction.getId());
|
||||
secondAction = (JpaAction) deploymentManagement.findActionWithDetails(secondAction.getId()).get();
|
||||
secondAction = (JpaAction) deploymentManagement.findActionWithDetails(secondAction.getId()).orElseThrow();
|
||||
// confirm cancellation
|
||||
controllerManagement.addCancelActionStatus(ActionStatusCreate.builder()
|
||||
.actionId(secondAction.getId()).status(Status.CANCELED).build());
|
||||
@@ -375,14 +369,14 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
// we cancel first -> back to installed
|
||||
deploymentManagement.cancelAction(firstAction.getId());
|
||||
firstAction = (JpaAction) deploymentManagement.findActionWithDetails(firstAction.getId()).get();
|
||||
firstAction = (JpaAction) deploymentManagement.findActionWithDetails(firstAction.getId()).orElseThrow();
|
||||
// confirm cancellation
|
||||
controllerManagement.addCancelActionStatus(
|
||||
ActionStatusCreate.builder().actionId(firstAction.getId()).status(Status.CANCELED).build());
|
||||
assertThat(actionStatusRepository.findAll()).as("wrong size of action status").hasSize(9);
|
||||
assertThat(deploymentManagement.findAssignedDistributionSet("4712")).as("wrong assigned ds")
|
||||
.contains(dsInstalled);
|
||||
assertThat(targetManagement.getByControllerId("4712").getUpdateStatus()).as("wrong update status")
|
||||
assertThat(deploymentManagement.findAssignedDistributionSet("4712")).as("wrong assigned ds").contains(dsInstalled);
|
||||
assertThat(targetManagement.getByControllerId("4712").getUpdateStatus())
|
||||
.as("wrong update status")
|
||||
.isEqualTo(TargetUpdateStatus.IN_SYNC);
|
||||
}
|
||||
|
||||
@@ -413,12 +407,12 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
// we cancel first -> second is left
|
||||
deploymentManagement.cancelAction(firstAction.getId());
|
||||
// confirm cancellation
|
||||
firstAction = (JpaAction) deploymentManagement.findActionWithDetails(firstAction.getId()).get();
|
||||
controllerManagement.addCancelActionStatus(
|
||||
ActionStatusCreate.builder().actionId(firstAction.getId()).status(Status.CANCELED).build());
|
||||
firstAction = (JpaAction) deploymentManagement.findActionWithDetails(firstAction.getId()).orElseThrow();
|
||||
controllerManagement.addCancelActionStatus(ActionStatusCreate.builder().actionId(firstAction.getId()).status(Status.CANCELED).build());
|
||||
assertThat(actionStatusRepository.findAll()).as("wrong size of action status").hasSize(7);
|
||||
assertThat(deploymentManagement.findAssignedDistributionSet("4712")).as("wrong assigned ds").contains(dsSecond);
|
||||
assertThat(targetManagement.getByControllerId("4712").getUpdateStatus()).as("wrong target update status")
|
||||
assertThat(targetManagement.getByControllerId("4712").getUpdateStatus())
|
||||
.as("wrong target update status")
|
||||
.isEqualTo(TargetUpdateStatus.PENDING);
|
||||
|
||||
// we cancel second -> remain assigned until finished cancellation
|
||||
@@ -427,8 +421,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(actionStatusRepository.findAll()).as("wrong size of action status").hasSize(8);
|
||||
assertThat(deploymentManagement.findAssignedDistributionSet("4712")).as("wrong assigned ds").contains(dsSecond);
|
||||
// confirm cancellation
|
||||
controllerManagement.addCancelActionStatus(
|
||||
ActionStatusCreate.builder().actionId(secondAction.getId()).status(Status.CANCELED).build());
|
||||
controllerManagement.addCancelActionStatus(ActionStatusCreate.builder().actionId(secondAction.getId()).status(Status.CANCELED).build());
|
||||
// cancelled success -> back to dsInstalled
|
||||
assertThat(deploymentManagement.findAssignedDistributionSet("4712"))
|
||||
.as("wrong installed ds")
|
||||
@@ -469,10 +462,10 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
// verify
|
||||
assertThat(assigningAction.getStatus()).as("wrong size of status").isEqualTo(Status.CANCELED);
|
||||
assertThat(deploymentManagement.findAssignedDistributionSet("4712")).as("wrong assigned ds")
|
||||
.contains(dsInstalled);
|
||||
assertThat(targetManagement.getByControllerId("4712").getUpdateStatus()).as("wrong target update status")
|
||||
.isEqualTo(TargetUpdateStatus.IN_SYNC);
|
||||
assertThat(deploymentManagement.findAssignedDistributionSet("4712"))
|
||||
.as("wrong assigned ds").contains(dsInstalled);
|
||||
assertThat(targetManagement.getByControllerId("4712").getUpdateStatus())
|
||||
.as("wrong target update status").isEqualTo(TargetUpdateStatus.IN_SYNC);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -482,8 +475,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
void forceQuitNotAllowedThrowsException() {
|
||||
final Action action = prepareFinishedUpdate("4712", "installed", true);
|
||||
// verify initial status
|
||||
assertThat(targetManagement.getByControllerId("4712").getUpdateStatus()).as("wrong update status")
|
||||
.isEqualTo(TargetUpdateStatus.IN_SYNC);
|
||||
assertThat(targetManagement.getByControllerId("4712").getUpdateStatus())
|
||||
.as("wrong update status").isEqualTo(TargetUpdateStatus.IN_SYNC);
|
||||
|
||||
final Target target = action.getTarget();
|
||||
final DistributionSet ds = testdataFactory.createDistributionSet("newDS", true);
|
||||
@@ -600,8 +593,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
@Expect(type = TenantConfigurationCreatedEvent.class, count = 1),
|
||||
@Expect(type = TenantConfigurationUpdatedEvent.class, count = 1) })
|
||||
void assignDistributionSetAndAutoCloseActiveActions() {
|
||||
tenantConfigurationManagement
|
||||
.addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, true);
|
||||
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, true);
|
||||
|
||||
try {
|
||||
final List<Target> targets = testdataFactory.createTargets(10);
|
||||
@@ -623,8 +615,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
.as("InstallationDate not set").allMatch(target -> (target.getInstallationDate() == null));
|
||||
|
||||
} finally {
|
||||
tenantConfigurationManagement
|
||||
.addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, false);
|
||||
tenantConfigurationManagement.addOrUpdateConfiguration(TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -719,12 +710,10 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
void cancelMultiAssignmentActions() {
|
||||
final List<Target> targets = testdataFactory.createTargets(1);
|
||||
final List<DistributionSet> distributionSets = testdataFactory.createDistributionSets(4);
|
||||
final List<DeploymentRequest> deploymentRequests = createAssignmentRequests(distributionSets, targets, 34,
|
||||
false);
|
||||
final List<DeploymentRequest> deploymentRequests = createAssignmentRequests(distributionSets, targets, 34, false);
|
||||
|
||||
enableMultiAssignments();
|
||||
final List<DistributionSetAssignmentResult> results = deploymentManagement
|
||||
.assignDistributionSets(deploymentRequests);
|
||||
final List<DistributionSetAssignmentResult> results = deploymentManagement.assignDistributionSets(deploymentRequests);
|
||||
|
||||
assertThat(getResultingActionCount(results)).isEqualTo(deploymentRequests.size());
|
||||
|
||||
@@ -732,8 +721,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
targets.forEach(target ->
|
||||
deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).forEach(action -> {
|
||||
assertThat(action.getDistributionSet().getId()).isIn(dsIds);
|
||||
assertThat(action.getInitiatedBy()).as("Should be Initiated by current user")
|
||||
.isEqualTo(tenantAware.getCurrentUsername());
|
||||
assertThat(action.getInitiatedBy())
|
||||
.as("Should be Initiated by current user").isEqualTo(tenantAware.getCurrentUsername());
|
||||
deploymentManagement.cancelAction(action.getId());
|
||||
}));
|
||||
}
|
||||
@@ -757,8 +746,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
.isThrownBy(() -> deploymentManagement.assignDistributionSets(deploymentRequests));
|
||||
|
||||
enableMultiAssignments();
|
||||
assertThat(getResultingActionCount(
|
||||
deploymentManagement.assignDistributionSets(Arrays.asList(targetToDS0, targetToDS1)))).isEqualTo(2);
|
||||
assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(Arrays.asList(targetToDS0, targetToDS1)))).isEqualTo(2);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -798,16 +786,15 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
|
||||
enableConfirmationFlow();
|
||||
List<DistributionSetAssignmentResult> results = assignDistributionSetToTargets(distributionSet, controllerIds,
|
||||
confirmationRequired);
|
||||
List<DistributionSetAssignmentResult> results = assignDistributionSetToTargets(distributionSet, controllerIds, confirmationRequired);
|
||||
|
||||
assertThat(getResultingActionCount(results)).isEqualTo(controllerIds.size());
|
||||
|
||||
controllerIds.forEach(controllerId ->
|
||||
deploymentManagement.findActionsByTarget(controllerId, PAGE).forEach(action -> {
|
||||
assertThat(action.getDistributionSet().getId()).isIn(distributionSet.getId());
|
||||
assertThat(action.getInitiatedBy()).as("Should be Initiated by current user")
|
||||
.isEqualTo(tenantAware.getCurrentUsername());
|
||||
assertThat(action.getInitiatedBy())
|
||||
.as("Should be Initiated by current user").isEqualTo(tenantAware.getCurrentUsername());
|
||||
if (confirmationRequired) {
|
||||
assertThat(action.getStatus()).isEqualTo(Status.WAIT_FOR_CONFIRMATION);
|
||||
} else {
|
||||
@@ -833,8 +820,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
|
||||
assignDistributionSets(Collections
|
||||
.singletonList(DeploymentRequest.builder(target.getControllerId(), distributionSet.getId())
|
||||
assignDistributionSets(List.of(
|
||||
DeploymentRequest.builder(target.getControllerId(), distributionSet.getId())
|
||||
.confirmationRequired(confirmationRequired).build()));
|
||||
|
||||
assertThat(deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent()).hasSize(1)
|
||||
@@ -877,8 +864,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
final DistributionSet secondDs = testdataFactory.createDistributionSet();
|
||||
|
||||
enableConfirmationFlow();
|
||||
final List<Action> resultActions = assignDistributionSet(firstDs.getId(), target.getControllerId())
|
||||
.getAssignedEntity();
|
||||
final List<Action> resultActions = assignDistributionSet(firstDs.getId(), target.getControllerId()).getAssignedEntity();
|
||||
assertThat(resultActions).hasSize(1);
|
||||
|
||||
assertThat(resultActions.get(0)).satisfies(action -> {
|
||||
@@ -886,23 +872,19 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertThat(action.getStatus()).isEqualTo(Status.WAIT_FOR_CONFIRMATION);
|
||||
});
|
||||
|
||||
final List<Action> resultActions2 = assignDistributionSet(secondDs.getId(), target.getControllerId())
|
||||
.getAssignedEntity();
|
||||
|
||||
final List<Action> resultActions2 = assignDistributionSet(secondDs.getId(), target.getControllerId()).getAssignedEntity();
|
||||
assertThat(resultActions2).hasSize(1);
|
||||
assertThat(resultActions2.get(0)).satisfies(action -> {
|
||||
assertThat(action.getDistributionSet().getId()).isEqualTo(secondDs.getId());
|
||||
assertThat(action.getStatus()).isEqualTo(Status.WAIT_FOR_CONFIRMATION);
|
||||
});
|
||||
|
||||
final List<Action> actions = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE)
|
||||
.getContent();
|
||||
final List<Action> actions = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent();
|
||||
assertThat(actions).hasSize(2)
|
||||
.anyMatch(action -> Objects.equals(action.getDistributionSet().getId(), firstDs.getId())
|
||||
&& action.getStatus() == Status.CANCELING)
|
||||
.anyMatch(action -> Objects.equals(action.getDistributionSet().getId(), secondDs.getId())
|
||||
&& action.getStatus() == Status.WAIT_FOR_CONFIRMATION);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -915,25 +897,23 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
final DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
|
||||
final List<DistributionSetAssignmentResult> results = Stream
|
||||
.concat(assignDistributionSetToTargets(distributionSet, targets1, true).stream(), //
|
||||
assignDistributionSetToTargets(distributionSet, targets2, false).stream()) //
|
||||
.concat(assignDistributionSetToTargets(distributionSet, targets1, true).stream(),
|
||||
assignDistributionSetToTargets(distributionSet, targets2, false).stream())
|
||||
.toList();
|
||||
|
||||
final List<String> controllerIds = Stream.concat(targets1.stream(), targets2.stream()).toList();
|
||||
|
||||
assertThat(getResultingActionCount(results)).isEqualTo(controllerIds.size());
|
||||
|
||||
controllerIds.forEach(controllerId ->
|
||||
deploymentManagement.findActionsByTarget(controllerId, PAGE).forEach(action -> {
|
||||
assertThat(action.getDistributionSet().getId()).isIn(distributionSet.getId());
|
||||
assertThat(action.getInitiatedBy()).as("Should be Initiated by current user")
|
||||
.isEqualTo(tenantAware.getCurrentUsername());
|
||||
assertThat(action.getInitiatedBy())
|
||||
.as("Should be Initiated by current user").isEqualTo(tenantAware.getCurrentUsername());
|
||||
assertThat(action.getStatus()).isEqualTo(RUNNING);
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate Assignments are removed from a request when multiassignment is disabled, otherwise not
|
||||
* Duplicate Assignments are removed from a request when multi-assignment is disabled, otherwise not
|
||||
*/
|
||||
@Test
|
||||
@ExpectEvents({
|
||||
@@ -950,18 +930,14 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
void duplicateAssignmentsInRequestAreRemovedIfMultiassignmentEnabled() {
|
||||
final String targetId = testdataFactory.createTarget().getControllerId();
|
||||
final Long dsId = testdataFactory.createDistributionSet().getId();
|
||||
final List<DeploymentRequest> twoEqualAssignments = Collections.nCopies(2,
|
||||
DeploymentRequest.builder(targetId, dsId).build());
|
||||
|
||||
assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(twoEqualAssignments)))
|
||||
.isEqualTo(1);
|
||||
final List<DeploymentRequest> twoEqualAssignments = Collections.nCopies(2, DeploymentRequest.builder(targetId, dsId).build());
|
||||
assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(twoEqualAssignments))).isEqualTo(1);
|
||||
|
||||
enableMultiAssignments();
|
||||
final List<DeploymentRequest> twoEqualAssignmentsWithWeight = Collections.nCopies(2,
|
||||
DeploymentRequest.builder(targetId, dsId).weight(555).build());
|
||||
final List<DeploymentRequest> twoEqualAssignmentsWithWeight = Collections.nCopies(
|
||||
2, DeploymentRequest.builder(targetId, dsId).weight(555).build());
|
||||
|
||||
assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(twoEqualAssignmentsWithWeight)))
|
||||
.isEqualTo(1);
|
||||
assertThat(getResultingActionCount(deploymentManagement.assignDistributionSets(twoEqualAssignmentsWithWeight))).isEqualTo(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -985,8 +961,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
final List<DeploymentRequest> deploymentRequests = new ArrayList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
final Long dsId = testdataFactory.createDistributionSet().getId();
|
||||
deploymentRequests.add(
|
||||
DeploymentRequest.builder(controllerId, dsId).weight(24).build());
|
||||
deploymentRequests.add(DeploymentRequest.builder(controllerId, dsId).weight(24).build());
|
||||
}
|
||||
|
||||
enableMultiAssignments();
|
||||
@@ -1039,8 +1014,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
void weightValidatedAndSaved() {
|
||||
final String targetId = testdataFactory.createTarget().getControllerId();
|
||||
final Long dsId = testdataFactory.createDistributionSet().getId();
|
||||
final DeploymentRequest valideRequest1 = DeploymentRequest.builder(targetId, dsId).weight(Action.WEIGHT_MAX).build();
|
||||
final DeploymentRequest valideRequest2 = DeploymentRequest.builder(targetId, dsId).weight(Action.WEIGHT_MIN).build();
|
||||
final DeploymentRequest validRequest1 = DeploymentRequest.builder(targetId, dsId).weight(Action.WEIGHT_MAX).build();
|
||||
final DeploymentRequest validRequest2 = DeploymentRequest.builder(targetId, dsId).weight(Action.WEIGHT_MIN).build();
|
||||
final DeploymentRequest weightTooLow = DeploymentRequest.builder(targetId, dsId).weight(Action.WEIGHT_MIN - 1).build();
|
||||
final DeploymentRequest weightTooHigh = DeploymentRequest.builder(targetId, dsId).weight(Action.WEIGHT_MAX + 1).build();
|
||||
enableMultiAssignments();
|
||||
@@ -1051,21 +1026,17 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
Assertions.assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(
|
||||
() -> deploymentManagement.assignDistributionSets(deploymentRequestsTooHigh));
|
||||
final Long validActionId1 = getFirstAssignedAction(
|
||||
deploymentManagement.assignDistributionSets(Collections.singletonList(valideRequest1)).get(0)).getId();
|
||||
deploymentManagement.assignDistributionSets(Collections.singletonList(validRequest1)).get(0)).getId();
|
||||
final Long validActionId2 = getFirstAssignedAction(
|
||||
deploymentManagement.assignDistributionSets(Collections.singletonList(valideRequest2)).get(0)).getId();
|
||||
deploymentManagement.assignDistributionSets(Collections.singletonList(validRequest2)).get(0)).getId();
|
||||
assertThat(actionRepository.findById(validActionId1).get().getWeight()).get().isEqualTo(Action.WEIGHT_MAX);
|
||||
assertThat(actionRepository.findById(validActionId2).get().getWeight()).get().isEqualTo(Action.WEIGHT_MIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* test a simple deployment by calling the
|
||||
* {@link TargetRepository#assignDistributionSet(DistributionSet, Iterable)} and
|
||||
* Test a simple deployment by calling the {@link TargetRepository#assignDistributionSet(DistributionSet, Iterable)} and
|
||||
* checking the active action and the action history of the targets.
|
||||
*/
|
||||
/**
|
||||
* Simple deployment or distribution set to target assignment test.
|
||||
*/
|
||||
@Test
|
||||
@ExpectEvents({
|
||||
@Expect(type = TargetAssignDistributionSetEvent.class, count = 1),
|
||||
@@ -1077,7 +1048,6 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
@Expect(type = ActionCreatedEvent.class, count = 20),
|
||||
@Expect(type = TargetUpdatedEvent.class, count = 20) })
|
||||
void assignDistributionSet2Targets() {
|
||||
|
||||
final String myCtrlIDPref = "myCtrlID";
|
||||
final Iterable<Target> savedNakedTargets = testdataFactory.createTargets(10, myCtrlIDPref, "first description");
|
||||
|
||||
@@ -1152,16 +1122,14 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
.as("expected IncompleteDistributionSetException")
|
||||
.isThrownBy(() -> assignDistributionSet(incomplete, targets));
|
||||
|
||||
final DistributionSet nowComplete = distributionSetManagement.assignSoftwareModules(incomplete.getId(),
|
||||
Set.of(os.getId()));
|
||||
final DistributionSet nowComplete = distributionSetManagement.assignSoftwareModules(incomplete.getId(), Set.of(os.getId()));
|
||||
|
||||
assertThat(assignDistributionSet(nowComplete, targets).getAssigned()).as("assign ds doesn't work")
|
||||
.isEqualTo(10);
|
||||
assertThat(assignDistributionSet(nowComplete, targets).getAssigned()).as("assign ds doesn't work").isEqualTo(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiple deployments or distribution set to target assignment test. Expected behaviour is that a new deployment
|
||||
* overides unfinished old one which are canceled as part of the operation.
|
||||
* overrides unfinished old one which are canceled as part of the operation.
|
||||
*/
|
||||
@Test
|
||||
@ExpectEvents({
|
||||
@@ -1287,10 +1255,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
.as("no actions should be active").isEmpty();
|
||||
}
|
||||
|
||||
// deploy dsA to the target which already have dsB deployed -> must
|
||||
// remove updActB from
|
||||
// activeActions, add a corresponding cancelAction and another
|
||||
// UpdateAction for dsA
|
||||
// deploy dsA to the target which already have dsB deployed -> must remove updActB from
|
||||
// activeActions, add a corresponding cancelAction and another UpdateAction for dsA
|
||||
final List<Target> deployed2DS = assignDistributionSet(dsA, deployResWithDsB.getDeployedTargets())
|
||||
.getAssignedEntity().stream().map(Action::getTarget).toList();
|
||||
findActionsByDistributionSet(pageRequest, dsA.getId()).getContent().get(1);
|
||||
@@ -1316,14 +1282,10 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
}
|
||||
|
||||
/**
|
||||
* test the deletion of {@link DistributionSet}s including exception in case of
|
||||
* Test the deletion of {@link DistributionSet}s including exception in case of
|
||||
* {@link Target}s are assigned by {@link Target#getAssignedDistributionSet()}
|
||||
* or {@link Target#getInstalledDistributionSet()}
|
||||
*/
|
||||
/**
|
||||
* Deletes distribution set. Expected behaviour is that a soft delete is performed
|
||||
* if the DS is assigned to a target and a hard delete if the DS is not in use at all.
|
||||
*/
|
||||
@Test
|
||||
void deleteDistributionSet() {
|
||||
final PageRequest pageRequest = PageRequest.of(0, 100, Direction.ASC, "id");
|
||||
@@ -1406,7 +1368,6 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
*/
|
||||
@Test
|
||||
void alternatingAssignmentAndAddUpdateActionStatus() {
|
||||
|
||||
final DistributionSet dsA = testdataFactory.createDistributionSet("a");
|
||||
final DistributionSet dsB = testdataFactory.createDistributionSet("b");
|
||||
List<Target> targs = Collections.singletonList(testdataFactory.createTarget("target-id-A"));
|
||||
@@ -1445,29 +1406,24 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
"active target actions are wrong");
|
||||
|
||||
assertEquals(TargetUpdateStatus.IN_SYNC, targ.getUpdateStatus(), "tagret update status is not correct");
|
||||
assertEquals(dsA, deploymentManagement.findAssignedDistributionSet(targ.getControllerId()).get(),
|
||||
"wrong assigned ds");
|
||||
assertEquals(dsA, deploymentManagement.findInstalledDistributionSet(targ.getControllerId()).get(),
|
||||
"wrong installed ds");
|
||||
assertEquals(dsA, deploymentManagement.findAssignedDistributionSet(targ.getControllerId()).get(), "wrong assigned ds");
|
||||
assertEquals(dsA, deploymentManagement.findInstalledDistributionSet(targ.getControllerId()).get(), "wrong installed ds");
|
||||
|
||||
targs = assignDistributionSet(dsB.getId(), "target-id-A").getAssignedEntity().stream().map(Action::getTarget)
|
||||
.toList();
|
||||
targs = assignDistributionSet(dsB.getId(), "target-id-A").getAssignedEntity().stream().map(Action::getTarget).toList();
|
||||
implicitLock(dsB);
|
||||
|
||||
targ = targs.iterator().next();
|
||||
|
||||
assertEquals(1, deploymentManagement.findActiveActionsByTarget(targ.getControllerId(), PAGE).getTotalElements(),
|
||||
"active actions are wrong");
|
||||
assertEquals(TargetUpdateStatus.PENDING,
|
||||
targetManagement.getByControllerId(targ.getControllerId()).getUpdateStatus(),
|
||||
assertEquals(TargetUpdateStatus.PENDING, targetManagement.getByControllerId(targ.getControllerId()).getUpdateStatus(),
|
||||
"target status is wrong");
|
||||
assertEquals(dsB, deploymentManagement.findAssignedDistributionSet(targ.getControllerId()).get(),
|
||||
"wrong assigned ds");
|
||||
assertEquals(dsA.getId(),
|
||||
deploymentManagement.findInstalledDistributionSet(targ.getControllerId()).get().getId(),
|
||||
assertEquals(dsA.getId(), deploymentManagement.findInstalledDistributionSet(targ.getControllerId()).get().getId(),
|
||||
"Installed ds is wrong");
|
||||
assertEquals(dsB, deploymentManagement.findActiveActionsByTarget(targ.getControllerId(), PAGE).getContent()
|
||||
.get(0).getDistributionSet(), "Active ds is wrong");
|
||||
assertEquals(dsB, deploymentManagement.findActiveActionsByTarget(targ.getControllerId(), PAGE).getContent().get(0).getDistributionSet(),
|
||||
"Active ds is wrong");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1545,19 +1501,17 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
*/
|
||||
@Test
|
||||
void testAlreadyAssignedAndAssignedActionsInAssignmentResult() {
|
||||
// create target1, distributionSet, assign ds to target1 and finish
|
||||
// update (close all actions)
|
||||
// create target1, distributionSet, assign ds to target1 and finish update (close all actions)
|
||||
final Action action = prepareFinishedUpdate("target1", "ds", false);
|
||||
final Target target2 = testdataFactory.createTarget("target2");
|
||||
final Target target3 = testdataFactory.createTarget("target3");
|
||||
|
||||
// assign ds to target2, but don't finish update (actions should be
|
||||
// still open)
|
||||
// assign ds to target2, but don't finish update (actions should be still open)
|
||||
assignDistributionSet(action.getDistributionSet().getId(), target2.getControllerId());
|
||||
|
||||
final DistributionSetAssignmentResult assignmentResult = assignDistributionSet(
|
||||
action.getDistributionSet().getId(),
|
||||
Arrays.asList(action.getTarget().getControllerId(), target3.getControllerId()), ActionType.FORCED);
|
||||
List.of(action.getTarget().getControllerId(), target3.getControllerId()), ActionType.FORCED);
|
||||
|
||||
assertThat(assignmentResult).isNotNull();
|
||||
assertThat(assignmentResult.getTotal()).as("Total count of assigned and already assigned targets").isEqualTo(2);
|
||||
@@ -1567,8 +1521,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
final DistributionSet actionDistributionSet = distributionSetRepository.getById(action.getDistributionSet().getId());
|
||||
assertThat(assignmentResult.getAssignedEntity()).allMatch(
|
||||
a -> a.getTarget().equals(target3) && a.getDistributionSet().equals(actionDistributionSet));
|
||||
assertThat(assignmentResult.getAssignedEntity())
|
||||
.noneMatch(a -> a.getTarget().getControllerId().equals("target1"));
|
||||
assertThat(assignmentResult.getAssignedEntity()).noneMatch(a -> a.getTarget().getControllerId().equals("target1"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1580,8 +1533,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
|
||||
// create assigned DS
|
||||
final Target savedTarget = testdataFactory.createTarget();
|
||||
DistributionSetAssignmentResult assignmentResult = assignDistributionSet(dsToTargetAssigned.getId(),
|
||||
savedTarget.getControllerId());
|
||||
DistributionSetAssignmentResult assignmentResult = assignDistributionSet(dsToTargetAssigned.getId(), savedTarget.getControllerId());
|
||||
assertThat(assignmentResult.getAssignedEntity()).hasSize(1);
|
||||
|
||||
assignmentResult = assignDistributionSet(dsToTargetAssigned.getId(), savedTarget.getControllerId());
|
||||
@@ -1601,8 +1553,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
final List<DeploymentRequest> deploymentRequests = new ArrayList<>();
|
||||
for (int i = 0; i < quotaManagement.getMaxTargetDistributionSetAssignmentsPerManualAssignment(); i++) {
|
||||
final Target target = testdataFactory.createTarget("test-target-" + i, "test-target-" + i, targetType);
|
||||
final DeploymentRequest deployment = DeploymentRequest
|
||||
.builder(target.getControllerId(), ds.getId()).build();
|
||||
final DeploymentRequest deployment = DeploymentRequest.builder(target.getControllerId(), ds.getId()).build();
|
||||
deploymentRequests.add(deployment);
|
||||
}
|
||||
|
||||
@@ -1688,7 +1639,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
assertEquals(20, actions);
|
||||
|
||||
// extract the first 5 action ids
|
||||
List<Action> firstSample = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent();
|
||||
final List<Action> firstSample = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent();
|
||||
List<Long> shouldBePurgedActionsList = firstSample.stream().map(Identifiable::getId).limit(5).toList();
|
||||
|
||||
DistributionSet exceededQuotaDsAssign = testdataFactory.createDistributionSet("exceededQuotaAssignment");
|
||||
@@ -1706,7 +1657,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
actions = deploymentManagement.countActionsByTarget(target.getControllerId());
|
||||
assertEquals(16, actions);
|
||||
|
||||
List<Action> actionsList = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent();
|
||||
final List<Action> actionsList = deploymentManagement.findActionsByTarget(target.getControllerId(), PAGE).getContent();
|
||||
// first 5 should have been purged so the first actionId should be the last purged action id + 1
|
||||
assertEquals(shouldBePurgedActionsList.get(shouldBePurgedActionsList.size() - 1) + 1, actionsList.get(0).getId());
|
||||
assertEquals(firstSample.get(firstSample.size() - 1).getId() + 1, actionsList.get(15).getId());
|
||||
@@ -1717,9 +1668,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target target = testdataFactory.createTarget();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
Rollout rollout = testdataFactory.createRolloutByVariables("rollout-" + i, "Description", 1, "controllerId==" + target
|
||||
.getControllerId(),
|
||||
distributionSet, "50", "50");
|
||||
Rollout rollout = testdataFactory.createRolloutByVariables(
|
||||
"rollout-" + i, "Description", 1, "controllerId==" + target.getControllerId(), distributionSet, "50", "50");
|
||||
rolloutManagement.start(rollout.getId());
|
||||
rolloutHandler.handleAll();
|
||||
}
|
||||
@@ -1729,9 +1679,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
List<Long> shouldBePurgedActionsList = firstSample.stream().map(Identifiable::getId).limit(5).toList();
|
||||
|
||||
DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
Rollout rollout = testdataFactory.createRolloutByVariables("rollout-quota", "Description", 1, "controllerId==" + target
|
||||
.getControllerId(),
|
||||
distributionSet, "50", "50");
|
||||
Rollout rollout = testdataFactory.createRolloutByVariables(
|
||||
"rollout-quota", "Description", 1, "controllerId==" + target.getControllerId(), distributionSet, "50", "50");
|
||||
rolloutManagement.start(rollout.getId());
|
||||
// don't assert quota exception here because rollout executor does not throw such in order to not interrupt other executions
|
||||
rolloutHandler.handleAll();
|
||||
@@ -1747,7 +1696,6 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
// first 5 should have been purged so the first actionId should be the last purged action id + 1
|
||||
assertEquals(shouldBePurgedActionsList.get(shouldBePurgedActionsList.size() - 1) + 1, actionsList.get(0).getId());
|
||||
assertEquals(firstSample.get(firstSample.size() - 1).getId() + 1, actionsList.get(15).getId());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1755,9 +1703,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
final Target target = testdataFactory.createTarget();
|
||||
for (int i = 0; i < 18; i++) {
|
||||
DistributionSet distributionSet = testdataFactory.createDistributionSet();
|
||||
Rollout rollout = testdataFactory.createRolloutByVariables("rollout-" + i, "Description", 1, "controllerId==" + target
|
||||
.getControllerId(),
|
||||
distributionSet, "50", "50");
|
||||
Rollout rollout = testdataFactory.createRolloutByVariables(
|
||||
"rollout-" + i, "Description", 1, "controllerId==" + target.getControllerId(), distributionSet, "50", "50");
|
||||
rolloutManagement.start(rollout.getId());
|
||||
rolloutHandler.handleAll();
|
||||
}
|
||||
@@ -1781,8 +1728,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
private List<DeploymentRequest> createAssignmentRequests(final Collection<DistributionSet> distributionSets,
|
||||
final Collection<Target> targets, final int weight, final boolean confirmationRequired) {
|
||||
final List<DeploymentRequest> deploymentRequests = new ArrayList<>();
|
||||
distributionSets.forEach(ds -> targets.forEach(target -> deploymentRequests
|
||||
.add(DeploymentRequest.builder(target.getControllerId(), ds.getId())
|
||||
distributionSets.forEach(ds -> targets.forEach(target -> deploymentRequests.add(
|
||||
DeploymentRequest.builder(target.getControllerId(), ds.getId())
|
||||
.weight(weight).confirmationRequired(confirmationRequired).build())));
|
||||
return deploymentRequests;
|
||||
}
|
||||
@@ -1805,8 +1752,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
return action;
|
||||
}
|
||||
|
||||
private void assertDsExclusivelyAssignedToTargets(final List<Target> targets, final long dsId, final boolean active,
|
||||
final Status status) {
|
||||
private void assertDsExclusivelyAssignedToTargets(final List<Target> targets, final long dsId, final boolean active, final Status status) {
|
||||
final List<Action> assignment = findActionsByDistributionSet(PAGE, dsId).getContent();
|
||||
final String currentUsername = tenantAware.getCurrentUsername();
|
||||
|
||||
@@ -1819,8 +1765,8 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
assignment.stream().mapToLong(action -> action.getTarget().getId()).toArray());
|
||||
}
|
||||
|
||||
private List<DistributionSetAssignmentResult> assignDistributionSetToTargets(final DistributionSet distributionSet,
|
||||
final Iterable<String> targetIds, final boolean confirmationRequired) {
|
||||
private List<DistributionSetAssignmentResult> assignDistributionSetToTargets(
|
||||
final DistributionSet distributionSet, final Iterable<String> targetIds, final boolean confirmationRequired) {
|
||||
final List<DeploymentRequest> deploymentRequests = new ArrayList<>();
|
||||
for (final String controllerId : targetIds) {
|
||||
deploymentRequests.add(new DeploymentRequest(controllerId, distributionSet.getId(), ActionType.FORCED, 0,
|
||||
@@ -1846,11 +1792,11 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
* @param noOfDeployedTargets number of targets to which the created distribution sets assigned
|
||||
* @param noOfDistributionSets number of distribution sets
|
||||
* @param distributionSetPrefix prefix for the created distribution sets
|
||||
* @return the {@link DeploymentResult} containing all created targets, the
|
||||
* distribution sets, the corresponding IDs for later evaluation in
|
||||
* @return the {@link DeploymentResult} containing all created targets, the distribution sets, the corresponding IDs for later evaluation in
|
||||
* tests
|
||||
*/
|
||||
private DeploymentResult prepareComplexRepo(final String undeployedTargetPrefix, final int noOfUndeployedTargets,
|
||||
private DeploymentResult prepareComplexRepo(
|
||||
final String undeployedTargetPrefix, final int noOfUndeployedTargets,
|
||||
final String deployedTargetPrefix, final int noOfDeployedTargets, final int noOfDistributionSets,
|
||||
final String distributionSetPrefix) {
|
||||
final Iterable<Target> nakedTargets = testdataFactory.createTargets(noOfUndeployedTargets,
|
||||
@@ -1865,8 +1811,7 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
// assigning all DistributionSet to the Target in the list
|
||||
// deployedTargets
|
||||
for (final DistributionSet ds : dsList) {
|
||||
deployedTargets = assignDistributionSet(ds, deployedTargets).getAssignedEntity().stream()
|
||||
.map(Action::getTarget).toList();
|
||||
deployedTargets = assignDistributionSet(ds, deployedTargets).getAssignedEntity().stream().map(Action::getTarget).toList();
|
||||
implicitLock(ds);
|
||||
}
|
||||
|
||||
@@ -1874,11 +1819,9 @@ class DeploymentManagementTest extends AbstractJpaIntegrationTest {
|
||||
}
|
||||
|
||||
private Slice<Action> findActionsByDistributionSet(final Pageable pageable, final long distributionSetId) {
|
||||
distributionSetManagement.find(distributionSetId).orElseThrow(() ->
|
||||
new EntityNotFoundException(DistributionSet.class, distributionSetId));
|
||||
return actionRepository
|
||||
.findAll(byDistributionSetId(distributionSetId), pageable)
|
||||
.map(Action.class::cast);
|
||||
distributionSetManagement.find(distributionSetId)
|
||||
.orElseThrow(() -> new EntityNotFoundException(DistributionSet.class, distributionSetId));
|
||||
return actionRepository.findAll(byDistributionSetId(distributionSetId), pageable).map(Action.class::cast);
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
||||
@@ -27,18 +27,20 @@ import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.T
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
/**
|
||||
* Feature: Component Tests - Repository<br/>
|
||||
* Story: Tenant Configuration Management
|
||||
*/
|
||||
@TestPropertySource(properties = {"hawkbit.cache.JpaTenantConfiguration.spec=maximumSize=0"}) // disable cache, its async processed
|
||||
class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest implements EnvironmentAware {
|
||||
|
||||
private Environment environment;
|
||||
private Environment env;
|
||||
|
||||
@Override
|
||||
public void setEnvironment(final Environment environment) {
|
||||
this.environment = environment;
|
||||
public void setEnvironment(final Environment env) {
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,7 +48,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
|
||||
*/
|
||||
@Test
|
||||
void storeTenantSpecificConfigurationAsString() {
|
||||
final String envPropertyDefault = environment.getProperty("hawkbit.server.ddi.security.authentication.gatewaytoken.key");
|
||||
final String envPropertyDefault = env.getProperty("hawkbit.server.ddi.security.authentication.gatewaytoken.key");
|
||||
assertThat(envPropertyDefault).isNotNull();
|
||||
|
||||
// get the configuration from the system management
|
||||
@@ -56,7 +58,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
|
||||
assertThat(defaultConfigValue.isGlobal()).isTrue();
|
||||
assertThat(defaultConfigValue.getValue()).isEqualTo(envPropertyDefault);
|
||||
|
||||
// update the tenant specific configuration
|
||||
// update the tenant specific configuration, create
|
||||
final String newConfigurationValue = "thisIsAnotherTokenName";
|
||||
assertThat(newConfigurationValue).isNotEqualTo(defaultConfigValue.getValue());
|
||||
tenantConfigurationManagement.addOrUpdateConfiguration(
|
||||
@@ -68,6 +70,19 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
|
||||
|
||||
assertThat(updatedConfigurationValue.isGlobal()).isFalse();
|
||||
assertThat(updatedConfigurationValue.getValue()).isEqualTo(newConfigurationValue);
|
||||
|
||||
// update the tenant specific configuration, create
|
||||
final String newConfigurationValue2 = "thisIsAnotherTokenName2";
|
||||
assertThat(newConfigurationValue2).isNotEqualTo(updatedConfigurationValue.getValue());
|
||||
tenantConfigurationManagement.addOrUpdateConfiguration(
|
||||
TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, newConfigurationValue2);
|
||||
|
||||
// verify that new configuration value is used
|
||||
final TenantConfigurationValue<String> updatedConfigurationValue2 = tenantConfigurationManagement
|
||||
.getConfigurationValue(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, String.class);
|
||||
|
||||
assertThat(updatedConfigurationValue2.isGlobal()).isFalse();
|
||||
assertThat(updatedConfigurationValue2.getValue()).isEqualTo(newConfigurationValue2);
|
||||
// assertThat(tenantConfigurationManagement.getTenantConfigurations()).hasSize(1);
|
||||
}
|
||||
|
||||
@@ -94,7 +109,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
|
||||
*/
|
||||
@Test
|
||||
void batchUpdateTenantSpecificConfiguration() {
|
||||
Map<String, Serializable> configuration = new HashMap<>() {{
|
||||
final Map<String, Serializable> configuration = new HashMap<>() {{
|
||||
put(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, "token_123");
|
||||
put(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, true);
|
||||
}};
|
||||
@@ -185,8 +200,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
|
||||
|
||||
// delete the tenant specific configuration
|
||||
tenantConfigurationManagement.deleteConfiguration(configKey);
|
||||
// ensure that now gateway token is set again, because is deleted and
|
||||
// must be empty now
|
||||
// ensure that now gateway token is set again, because is deleted and must be empty now
|
||||
assertThat(tenantConfigurationManagement.getConfigurationValue(configKey, String.class).getValue()).isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -409,8 +409,7 @@ public abstract class AbstractIntegrationTest {
|
||||
return assignDistributionSet(pset, Collections.singletonList(target));
|
||||
}
|
||||
|
||||
protected DistributionSetAssignmentResult assignDistributionSet(final long dsId, final String targetId,
|
||||
final int weight) {
|
||||
protected DistributionSetAssignmentResult assignDistributionSet(final long dsId, final String targetId, final int weight) {
|
||||
return assignDistributionSet(dsId, Collections.singletonList(targetId), weight);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@ import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -55,6 +56,7 @@ public class CleanupTestExecutionListener extends AbstractTestExecutionListener
|
||||
log.error("Error while delete tenant", e);
|
||||
}
|
||||
});
|
||||
TenantAwareCacheManager.getInstance().evictTenant(null);
|
||||
// evict global cache
|
||||
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new CacheEvictEvent.Default(null, null, null));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user