Refactor tenant configuration management (#2840)

Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
Avgustin Marinov
2025-11-28 15:37:12 +02:00
committed by GitHub
parent 203598f3a4
commit b8a05e3cbf
14 changed files with 292 additions and 353 deletions

View File

@@ -188,7 +188,7 @@ class AutoActionCleanupTest extends AbstractJpaIntegrationTest {
assertThat(actionRepository.count()).isEqualTo(3);
// wait for expiry to elapse
Thread.sleep(800);
waitMillis(800);
autoActionCleanup.run();

View File

@@ -77,16 +77,19 @@ class DistributedLockTest extends AbstractJpaIntegrationTest {
}
@Bean
LockRepository lockRepository0(final DataSource dataSource, final LockProperties lockProperties, final PlatformTransactionManager txManager) {
LockRepository lockRepository0(final DataSource dataSource, final LockProperties lockProperties,
final PlatformTransactionManager txManager) {
return lockRepository(dataSource, lockProperties, txManager);
}
@Bean
LockRepository lockRepository1(final DataSource dataSource, final LockProperties lockProperties, final PlatformTransactionManager txManager) {
LockRepository lockRepository1(final DataSource dataSource, final LockProperties lockProperties,
final PlatformTransactionManager txManager) {
return lockRepository(dataSource, lockProperties, txManager);
}
private LockRepository lockRepository(final DataSource dataSource, final LockProperties lockProperties, final PlatformTransactionManager txManager) {
private LockRepository lockRepository(final DataSource dataSource, final LockProperties lockProperties,
final PlatformTransactionManager txManager) {
final DefaultLockRepository repository = new DistributedLockRepository(dataSource, lockProperties, txManager);
repository.setPrefix("SP_");
return repository;
@@ -96,7 +99,7 @@ class DistributedLockTest extends AbstractJpaIntegrationTest {
/**
* Test to verify that lock is kept while ping runs
*/
@SuppressWarnings({"java:S2925"})
@SuppressWarnings({ "java:S2925" })
@Test
void keepLockAlive() {
final LockRegistry lockRegistry0 = new JdbcLockRegistry(lockRepository0);
@@ -119,13 +122,13 @@ class DistributedLockTest extends AbstractJpaIntegrationTest {
final AtomicBoolean lock11Locked = new AtomicBoolean(); // state of the lock11
log.info("Starting test");
// service 0 must be able to lock lockKey0
assertThat(lock00.tryLock()).isTrue();
assertThat(lock00.tryLock()).isTrue();
try {
assertThat(lockRepository0.isAcquired(path0)).isTrue(); // check db state
final Thread lockThread1 = new Thread(() -> {
// asserts lockKey1 is free and could be locked
assertThat(lock11.tryLock()).isTrue();
assertThat(lock11.tryLock()).isTrue();
assertThat(lockRepository1.isAcquired(path1)).isTrue(); // check db state
try {
@@ -140,20 +143,14 @@ class DistributedLockTest extends AbstractJpaIntegrationTest {
assertThat(lock01.tryLock()).isFalse();
assertThat(lockRepository1.isAcquired(path0)).isFalse(); // check db state
try {
Thread.sleep(Math.min(1, lockProperties.getTtl() / 4));
} catch (final InterruptedException e) {
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
}
}
waitMillis(Math.min(1, lockProperties.getTtl() / 4));
}
} catch (final AssertionError e) {
} catch (final AssertionError e) {
log.error("lockRepository1 has locked lockKey0 which has to be in lockRepository0 possession!", e);
lock01Obtained.set(true);
lock01.unlock();
}
assertThat(lockRepository0.isAcquired(path1)).isFalse(); // check db state
assertThat(lockRepository1.isAcquired(path1)).isTrue(); // check db state
} finally {
@@ -183,13 +180,7 @@ class DistributedLockTest extends AbstractJpaIntegrationTest {
}
}
try {
Thread.sleep(Math.min(1, lockProperties.getTtl() / 4));
} catch (final InterruptedException e) {
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
}
}
waitMillis(Math.min(1, lockProperties.getTtl() / 4));
}
}
@@ -205,7 +196,7 @@ class DistributedLockTest extends AbstractJpaIntegrationTest {
assertThat(lock01Obtained).isFalse();
// assert that service 1 has been able to acquire the lock 1
assertThat(lock11Obtained).isTrue();
assertThat(lockRepository0.isAcquired(path0)).isTrue(); // check db state
assertThat(lockRepository1.isAcquired(path0)).isFalse(); // check db state
} finally {

View File

@@ -18,6 +18,8 @@ import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import lombok.NonNull;
import org.awaitility.Awaitility;
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
import org.eclipse.hawkbit.repository.exception.InvalidTenantConfigurationKeyException;
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValidatorException;
@@ -28,6 +30,7 @@ import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.T
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.opentest4j.AssertionFailedError;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
@@ -48,7 +51,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
}
@Override
public void setEnvironment(final Environment env) {
public void setEnvironment(@NonNull final Environment env) {
this.env = env;
}
@@ -86,12 +89,20 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
tenantConfigurationManagement.addOrUpdateConfiguration(
TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, newConfigurationValue2);
// sometimes it reads old value, maybe if read too early. wait to settle up?
waitMillis(100);
// 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);
try {
assertThat(updatedConfigurationValue2.getValue()).isEqualTo(newConfigurationValue2);
} catch (final AssertionFailedError e) {
Awaitility.await().atMost(Duration.ofSeconds(20)).pollInterval(Duration.ofMillis(100))
.untilAsserted(() -> assertThat(updatedConfigurationValue2.getValue()).isEqualTo(newConfigurationValue2));
}
}
/**
@@ -117,7 +128,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
*/
@Test
void batchUpdateTenantSpecificConfiguration() {
final Map<String, Serializable> configuration = new HashMap<>() {{
final Map<String, Object> configuration = new HashMap<>() {{
put(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, "token_123");
put(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, true);
}};
@@ -165,7 +176,7 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
*/
@Test
void batchWrongTenantConfigurationValueTypeThrowsException() {
final Map<String, Serializable> configuration = new HashMap<>() {{
final Map<String, Object> configuration = new HashMap<>() {{
put(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, "token_123");
put(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, true);
put(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_ENABLED, "wrong");
@@ -216,12 +227,19 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
* Test that an Exception is thrown, when an integer is stored but a string expected.
*/
@Test
void storesIntegerWhenStringIsExpected() {
final String configKey = TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY;
final Integer wrongDatType = 123;
assertThatThrownBy(() -> tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongDatType))
void storesStringWhenIntegerIsExpected() {
final String configKey = TenantConfigurationKey.ACTION_CLEANUP_ON_QUOTA_HIT_PERCENTAGE;
final String wrongDataType = "123f";
assertThatThrownBy(() -> tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongDataType))
.as("Should not have worked as integer is not a string")
.isInstanceOf(TenantConfigurationValidatorException.class);
final Integer correctDataType = 123;
tenantConfigurationManagement.addOrUpdateConfiguration(configKey, String.valueOf(correctDataType));
assertThat(tenantConfigurationManagement.getConfigurationValue(configKey, Integer.class).getValue()).isEqualTo(correctDataType);
tenantConfigurationManagement.addOrUpdateConfiguration(configKey, correctDataType);
assertThat(tenantConfigurationManagement.getConfigurationValue(configKey, Integer.class).getValue()).isEqualTo(correctDataType);
}
/**
@@ -240,10 +258,9 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
* Test that an Exception is thrown, when an integer is stored as PollingTime.
*/
@Test
void storesIntegerWhenPollingIntervalIsExpected() {
void storesBadPollingIntervalIsExpected() {
final String configKey = TenantConfigurationKey.POLLING_TIME;
final Integer wrongDataType = 123;
assertThatThrownBy(() -> tenantConfigurationManagement.addOrUpdateConfiguration(configKey, wrongDataType))
assertThatThrownBy(() -> tenantConfigurationManagement.addOrUpdateConfiguration(configKey, "wrongDataType"))
.as("Should not have worked as integer is not a time field")
.isInstanceOf(TenantConfigurationValidatorException.class);
}
@@ -350,16 +367,6 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
Assertions.assertEquals(2592000000L, autoCleanupDaysInMs);
}
@Test
void throwExceptionIfTryingToConvertOtherValueThanNumber() {
final String configKey = TenantConfigurationKey.ACTION_CLEANUP_AUTO_EXPIRY;
// set auto cleanup for 1 day in String ms
assertThatThrownBy(() ->
tenantConfigurationManagement.addOrUpdateConfiguration(configKey, "86400000"))
.as("Cannot convert the value 86400000 of type String to the type Long defined by the configuration key.")
.isInstanceOf(TenantConfigurationValidatorException.class);
}
private static Duration getDurationByTimeValues(final long hours, final long minutes, final long seconds) {
return Duration.ofHours(hours).plusMinutes(minutes).plusSeconds(seconds);
}

View File

@@ -18,7 +18,6 @@ import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import org.eclipse.hawkbit.context.AccessContext;
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
import org.eclipse.hawkbit.repository.jpa.AbstractJpaIntegrationTest;
import org.eclipse.hawkbit.repository.model.DistributionSet;