[#1651] Implement tenant level config for DistributionSet implicit lock (#1678)

enabled by default

Signed-off-by: Marinov Avgustin <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2024-03-08 12:43:53 +02:00
committed by GitHub
parent ce9918ce00
commit 9ac51ab988
12 changed files with 58 additions and 73 deletions

View File

@@ -35,5 +35,4 @@ public interface TenantConfiguration extends TenantAwareBaseEntity {
* @return value of the entry * @return value of the entry
*/ */
String getValue(); String getValue();
} }

View File

@@ -12,18 +12,19 @@ package org.eclipse.hawkbit.repository.model;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
/** /**
* Represents a tenant configuration value including some meta data * Represents a tenant configuration value including some meta data
* *
* @param <T> * @param <T> type of the configuration value
* type of the configuration value
*/ */
@Data @Data
@Builder @Builder
public final class TenantConfigurationValue<T extends Serializable> implements Serializable { public final class TenantConfigurationValue<T extends Serializable> implements Serializable {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private T value; private T value;

View File

@@ -9,10 +9,13 @@
*/ */
package org.eclipse.hawkbit.tenancy.configuration; package org.eclipse.hawkbit.tenancy.configuration;
import java.io.Serializable;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import lombok.Data;
import lombok.ToString;
import org.eclipse.hawkbit.ControllerPollProperties; import org.eclipse.hawkbit.ControllerPollProperties;
import org.eclipse.hawkbit.HawkbitServerProperties.Anonymous.Download; import org.eclipse.hawkbit.HawkbitServerProperties.Anonymous.Download;
import org.eclipse.hawkbit.repository.exception.InvalidTenantConfigurationKeyException; import org.eclipse.hawkbit.repository.exception.InvalidTenantConfigurationKeyException;
@@ -24,20 +27,14 @@ import org.springframework.context.ApplicationContext;
/** /**
* Properties for tenant configuration default values. * Properties for tenant configuration default values.
*
*/ */
@Data
@ToString
@ConfigurationProperties("hawkbit.server.tenant") @ConfigurationProperties("hawkbit.server.tenant")
public class TenantConfigurationProperties { public class TenantConfigurationProperties {
private final Map<String, TenantConfigurationKey> configuration = new HashMap<>(); private final Map<String, TenantConfigurationKey> configuration = new HashMap<>();
/**
* @return full map of all configured tenant properties
*/
public Map<String, TenantConfigurationKey> getConfiguration() {
return configuration;
}
/** /**
* @return full list of {@link TenantConfigurationKey}s * @return full list of {@link TenantConfigurationKey}s
*/ */
@@ -46,8 +43,7 @@ public class TenantConfigurationProperties {
} }
/** /**
* @param keyName * @param keyName name of the TenantConfigurationKey
* name of the TenantConfigurationKey
* @return the TenantConfigurationKey with the name keyName * @return the TenantConfigurationKey with the name keyName
*/ */
public TenantConfigurationKey fromKeyName(final String keyName) { public TenantConfigurationKey fromKeyName(final String keyName) {
@@ -59,8 +55,9 @@ public class TenantConfigurationProperties {
/** /**
* Tenant specific configurations which can be configured for each tenant * Tenant specific configurations which can be configured for each tenant
* separately by means of override of the system defaults. * separately by means of override of the system defaults.
*
*/ */
@Data
@ToString
public static class TenantConfigurationKey { public static class TenantConfigurationKey {
/** /**
@@ -158,59 +155,22 @@ public class TenantConfigurationProperties {
*/ */
public static final String USER_CONFIRMATION_ENABLED = "user.confirmation.flow.enabled"; public static final String USER_CONFIRMATION_ENABLED = "user.confirmation.flow.enabled";
/**
* Switch to enable/disable the implicit locking
*/
public static final String IMPLICIT_LOCK_ENABLED = "implicit.lock.enabled";
private String keyName; private String keyName;
private String defaultValue = ""; private String defaultValue = "";
private Class<?> dataType = String.class; private Class<? extends Serializable> dataType = String.class;
private Class<? extends TenantConfigurationValidator> validator = TenantConfigurationStringValidator.class; private Class<? extends TenantConfigurationValidator> validator = TenantConfigurationStringValidator.class;
public String getKeyName() {
return keyName;
}
public void setKeyName(final String keyName) {
this.keyName = keyName;
}
/** /**
* validates if a object matches the allowed data format of the corresponding key
* *
* @return the data type of the tenant configuration value. (e.g. * @param context application context
* Integer.class, String.class) * @param value which will be validated
*/ * @throws TenantConfigurationValidatorException is thrown, when object is invalid
@SuppressWarnings("unchecked")
public <T> Class<T> getDataType() {
return (Class<T>) dataType;
}
public void setDataType(final Class<?> dataType) {
this.dataType = dataType;
}
public String getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(final String defaultValue) {
this.defaultValue = defaultValue;
}
public Class<? extends TenantConfigurationValidator> getValidator() {
return validator;
}
public void setValidator(final Class<? extends TenantConfigurationValidator> validator) {
this.validator = validator;
}
/**
* validates if a object matches the allowed data format of the
* corresponding key
*
* @param context
* application context
* @param value
* which will be validated
* @throws TenantConfigurationValidatorException
* is thrown, when object is invalid
*/ */
public void validate(final ApplicationContext context, final Object value) { public void validate(final ApplicationContext context, final Object value) {
final TenantConfigurationValidator createdBean = context.getAutowireCapableBeanFactory() final TenantConfigurationValidator createdBean = context.getAutowireCapableBeanFactory()

View File

@@ -15,6 +15,8 @@ import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationPrope
import org.eclipse.hawkbit.repository.TenantConfigurationManagement; import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import java.io.Serializable;
/** /**
* A collection of static helper methods for the tenant configuration * A collection of static helper methods for the tenant configuration
*/ */
@@ -43,14 +45,18 @@ public final class TenantConfigHelper {
return new TenantConfigHelper(systemSecurityContext, tenantConfigurationManagement); return new TenantConfigHelper(systemSecurityContext, tenantConfigurationManagement);
} }
public <T extends Serializable> T getConfigValue(final String key, final Class<T> valueType) {
return systemSecurityContext
.runAsSystem(() -> tenantConfigurationManagement.getConfigurationValue(key, valueType).getValue());
}
/** /**
* Is multi-assignments enabled for the current tenant * Is multi-assignments enabled for the current tenant
* *
* @return is active * @return is active
*/ */
public boolean isMultiAssignmentsEnabled() { public boolean isMultiAssignmentsEnabled() {
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement return getConfigValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class);
.getConfigurationValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class).getValue());
} }
/** /**
@@ -59,7 +65,6 @@ public final class TenantConfigHelper {
* @return is enabled * @return is enabled
*/ */
public boolean isConfirmationFlowEnabled() { public boolean isConfirmationFlowEnabled() {
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement return getConfigValue(USER_CONFIRMATION_ENABLED, Boolean.class);
.getConfigurationValue(USER_CONFIRMATION_ENABLED, Boolean.class).getValue());
} }
} }

View File

@@ -22,7 +22,7 @@ import java.util.Optional;
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ToString @ToString(callSuper = true)
@Accessors(fluent = true) @Accessors(fluent = true)
public class GenericDistributionSetUpdate extends AbstractDistributionSetUpdateCreate<DistributionSetUpdate> public class GenericDistributionSetUpdate extends AbstractDistributionSetUpdateCreate<DistributionSetUpdate>
implements DistributionSetUpdate { implements DistributionSetUpdate {

View File

@@ -22,7 +22,7 @@ import java.util.Optional;
*/ */
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ToString @ToString(callSuper = true)
@Accessors(fluent = true) @Accessors(fluent = true)
public class GenericSoftwareModuleUpdate extends AbstractSoftwareModuleUpdateCreate<SoftwareModuleUpdate> public class GenericSoftwareModuleUpdate extends AbstractSoftwareModuleUpdateCreate<SoftwareModuleUpdate>
implements SoftwareModuleUpdate { implements SoftwareModuleUpdate {

View File

@@ -107,4 +107,9 @@ hawkbit.server.tenant.configuration.user-confirmation-enabled.defaultValue=false
hawkbit.server.tenant.configuration.user-confirmation-enabled.dataType=java.lang.Boolean hawkbit.server.tenant.configuration.user-confirmation-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.user-confirmation-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator hawkbit.server.tenant.configuration.user-confirmation-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
hawkbit.server.tenant.configuration.implicit-lock-enabled.keyName=user.confirmation.flow.enabled
hawkbit.server.tenant.configuration.implicit-lock-enabled.defaultValue=true
hawkbit.server.tenant.configuration.implicit-lock-enabled.dataType=java.lang.Boolean
hawkbit.server.tenant.configuration.implicit-lock-enabled.validator=org.eclipse.hawkbit.tenancy.configuration.validator.TenantConfigurationBooleanValidator
# Default tenant configuration - END # Default tenant configuration - END

View File

@@ -9,6 +9,7 @@
*/ */
package org.eclipse.hawkbit.repository.jpa.management; package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.IMPLICIT_LOCK_ENABLED;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED; import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED;
import java.io.Serializable; import java.io.Serializable;
@@ -371,8 +372,9 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
final AbstractDsAssignmentStrategy assignmentStrategy) { final AbstractDsAssignmentStrategy assignmentStrategy) {
final JpaDistributionSet distributionSet = final JpaDistributionSet distributionSet =
(JpaDistributionSet) distributionSetManagement.getValidAndComplete(dsId); (JpaDistributionSet) distributionSetManagement.getValidAndComplete(dsId);
// implicit lock // implicit lock
if (!distributionSet.isLocked()) { if (!distributionSet.isLocked() && getConfigValue(IMPLICIT_LOCK_ENABLED, Boolean.class)) {
// without new transaction DS changed event is not thrown // without new transaction DS changed event is not thrown
DeploymentHelper.runInNewTransaction(txManager, "Implicit lock", status -> { DeploymentHelper.runInNewTransaction(txManager, "Implicit lock", status -> {
distributionSetManagement.lock(distributionSet.getId()); distributionSetManagement.lock(distributionSet.getId());

View File

@@ -10,6 +10,7 @@
package org.eclipse.hawkbit.repository.jpa.management; package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate.addSuccessAndErrorConditionsAndActions; import static org.eclipse.hawkbit.repository.jpa.builder.JpaRolloutGroupCreate.addSuccessAndErrorConditionsAndActions;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.IMPLICIT_LOCK_ENABLED;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -17,7 +18,6 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -75,6 +75,7 @@ import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder; import org.eclipse.hawkbit.repository.model.helper.EventPublisherHolder;
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer; import org.eclipse.hawkbit.repository.rsql.VirtualPropertyReplacer;
import org.eclipse.hawkbit.security.SystemSecurityContext; import org.eclipse.hawkbit.security.SystemSecurityContext;
import org.eclipse.hawkbit.utils.TenantConfigHelper;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.ConcurrencyFailureException; import org.springframework.dao.ConcurrencyFailureException;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@@ -143,6 +144,8 @@ public class JpaRolloutManagement implements RolloutManagement {
private final ContextAware contextAware; private final ContextAware contextAware;
private final Database database; private final Database database;
private final TenantConfigHelper tenantConfigHelper;
public JpaRolloutManagement(final TargetManagement targetManagement, public JpaRolloutManagement(final TargetManagement targetManagement,
final DistributionSetManagement distributionSetManagement, final EventPublisherHolder eventPublisherHolder, final DistributionSetManagement distributionSetManagement, final EventPublisherHolder eventPublisherHolder,
final VirtualPropertyReplacer virtualPropertyReplacer, final Database database, final VirtualPropertyReplacer virtualPropertyReplacer, final Database database,
@@ -159,6 +162,8 @@ public class JpaRolloutManagement implements RolloutManagement {
this.systemSecurityContext = systemSecurityContext; this.systemSecurityContext = systemSecurityContext;
this.eventPublisherHolder = eventPublisherHolder; this.eventPublisherHolder = eventPublisherHolder;
this.contextAware = contextAware; this.contextAware = contextAware;
tenantConfigHelper = TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement);
} }
@Override @Override
@@ -225,7 +230,7 @@ public class JpaRolloutManagement implements RolloutManagement {
rollout.setTotalTargets(totalTargets); rollout.setTotalTargets(totalTargets);
// implicit lock // implicit lock
if (!distributionSet.isLocked()) { if (!distributionSet.isLocked() && tenantConfigHelper.getConfigValue(IMPLICIT_LOCK_ENABLED, Boolean.class)) {
distributionSetManagement.lock(distributionSet.getId()); distributionSetManagement.lock(distributionSet.getId());
} }

View File

@@ -9,6 +9,8 @@
*/ */
package org.eclipse.hawkbit.repository.jpa.management; package org.eclipse.hawkbit.repository.jpa.management;
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.IMPLICIT_LOCK_ENABLED;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -87,6 +89,8 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
private final Database database; private final Database database;
private final TenantConfigHelper tenantConfigHelper;
public JpaTargetFilterQueryManagement(final TargetFilterQueryRepository targetFilterQueryRepository, public JpaTargetFilterQueryManagement(final TargetFilterQueryRepository targetFilterQueryRepository,
final TargetManagement targetManagement, final VirtualPropertyReplacer virtualPropertyReplacer, final TargetManagement targetManagement, final VirtualPropertyReplacer virtualPropertyReplacer,
final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement, final DistributionSetManagement distributionSetManagement, final QuotaManagement quotaManagement,
@@ -103,6 +107,8 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
this.repositoryProperties = repositoryProperties; this.repositoryProperties = repositoryProperties;
this.systemSecurityContext = systemSecurityContext; this.systemSecurityContext = systemSecurityContext;
this.contextAware = contextAware; this.contextAware = contextAware;
tenantConfigHelper = TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement);
} }
@Override @Override
@@ -278,8 +284,9 @@ public class JpaTargetFilterQueryManagement implements TargetFilterQueryManageme
assertMaxTargetsQuota(targetFilterQuery.getQuery(), targetFilterQuery.getName(), update.getDsId()); assertMaxTargetsQuota(targetFilterQuery.getQuery(), targetFilterQuery.getName(), update.getDsId());
final JpaDistributionSet distributionSet = (JpaDistributionSet) distributionSetManagement final JpaDistributionSet distributionSet = (JpaDistributionSet) distributionSetManagement
.getValidAndComplete(update.getDsId()); .getValidAndComplete(update.getDsId());
// implicit lock // implicit lock
if (!distributionSet.isLocked()) { if (!distributionSet.isLocked() && tenantConfigHelper.getConfigValue(IMPLICIT_LOCK_ENABLED, Boolean.class)) {
distributionSetManagement.lock(distributionSet.getId()); distributionSetManagement.lock(distributionSet.getId());
} }

View File

@@ -130,7 +130,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
final String configurationKeyName) { final String configurationKeyName) {
final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName); final TenantConfigurationKey configurationKey = tenantConfigurationProperties.fromKeyName(configurationKeyName);
return getConfigurationValue(configurationKeyName, configurationKey.getDataType()); return getConfigurationValue(configurationKeyName, (Class<T>)configurationKey.getDataType());
} }
@Override @Override

View File

@@ -53,6 +53,7 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include;
* **pollingOverdueTime** - String, The configuration key 'pollingOverdueTime' defines the period of time after the SP server will recognize a target, which is not performing pull requests anymore. * **pollingOverdueTime** - String, The configuration key 'pollingOverdueTime' defines the period of time after the SP server will recognize a target, which is not performing pull requests anymore.
* **multi.assignments.enabled** - Boolean, The configuration key 'multi.assignments.enabled' defines if multiple distribution sets can be assigned to the same targets. * **multi.assignments.enabled** - Boolean, The configuration key 'multi.assignments.enabled' defines if multiple distribution sets can be assigned to the same targets.
* **batch.assignments.enabled** - Boolean, The configuration key 'batch.assignments.enabled' defines if distribution set can be assigned to multiple targets in a single batch message. * **batch.assignments.enabled** - Boolean, The configuration key 'batch.assignments.enabled' defines if distribution set can be assigned to multiple targets in a single batch message.
* **implicit.lock.enabled** - Boolean (true by default), The configuration key 'implicit.lock.enabled' defines if distribution set and their software modules shall be implicitly locked when assigned to target, rollout or target filter.
""", example = """ """, example = """
{ {
"value" : "", "value" : "",