Fix nop cache using (especially for testing) (#2841)
+ fix flaky tests that requires no caches Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
9
.github/workflows/.trivyignore
vendored
9
.github/workflows/.trivyignore
vendored
@@ -1,5 +1,4 @@
|
|||||||
# org.springframework:spring-web:5.3.31.RELEASE, ineffective vulnerability - hawkBit doesn't use beans of type HttpInvokerServiceExporter in applications
|
# org.eclipse.hawkbit:*
|
||||||
CVE-2016-1000027
|
# this trivy finding is false positive since the current master (and other, new temporary, branches) doesn't contain http dependencies anymore
|
||||||
|
# trivy detects 0-SNAPSHOT version and could not 'understand' that it is newer then 0.3.0M2 (since which the http dependencies were removed)
|
||||||
# org.yaml:snakeyaml:1.33, ineffective vulnerability - Not applicable. Applications does not consume user-provided YAML data
|
CVE-2019-10240
|
||||||
CVE-2022-1471
|
|
||||||
@@ -18,6 +18,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
|
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.NonNull;
|
import lombok.NonNull;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
@@ -27,6 +28,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.cache.Cache;
|
import org.springframework.cache.Cache;
|
||||||
import org.springframework.cache.CacheManager;
|
import org.springframework.cache.CacheManager;
|
||||||
import org.springframework.cache.caffeine.CaffeineCache;
|
import org.springframework.cache.caffeine.CaffeineCache;
|
||||||
|
import org.springframework.cache.support.AbstractValueAdaptingCache;
|
||||||
import org.springframework.context.event.EventListener;
|
import org.springframework.context.event.EventListener;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
import org.springframework.lang.Nullable;
|
import org.springframework.lang.Nullable;
|
||||||
@@ -122,6 +124,26 @@ public class TenantAwareCacheManager implements CacheManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nop if, null, blank, "nop", "none", "maximumSize=0" or "expireAfterWrite=0"
|
||||||
|
static boolean isNop(@Nullable String spec) {
|
||||||
|
if (spec == null || spec.isBlank()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
final String trimmed = spec.replaceAll("\\s", "");
|
||||||
|
return "nop".equalsIgnoreCase(trimmed) ||
|
||||||
|
"none".equalsIgnoreCase(trimmed) ||
|
||||||
|
(trimmed.contains("maximumSize=0") &&
|
||||||
|
("maximumSize=0".equals(trimmed) ||
|
||||||
|
trimmed.startsWith("maximumSize=0,") ||
|
||||||
|
trimmed.contains(",maximumSize=0,") ||
|
||||||
|
trimmed.endsWith(",maximumSize=0"))) ||
|
||||||
|
(trimmed.contains("expireAfterWrite=0") &&
|
||||||
|
("expireAfterWrite=0".equals(trimmed) ||
|
||||||
|
trimmed.startsWith("expireAfterWrite=0,") ||
|
||||||
|
trimmed.contains(",expireAfterWrite=0,") ||
|
||||||
|
trimmed.endsWith(",expireAfterWrite=0")));
|
||||||
|
}
|
||||||
|
|
||||||
public interface CacheEvictEvent {
|
public interface CacheEvictEvent {
|
||||||
|
|
||||||
String getTenant();
|
String getTenant();
|
||||||
@@ -161,6 +183,11 @@ public class TenantAwareCacheManager implements CacheManager {
|
|||||||
spec = defaultSpec;
|
spec = defaultSpec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// it seems that setting maximumSize=0 doesn't filly disables the Caffeine cache, so we explicitly check for Nop cache
|
||||||
|
if (isNop(spec)) {
|
||||||
|
log.info("Using NOP cache for tenant '{}' and cache '{}'", tenant, name);
|
||||||
|
return new Nop(name);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return new CaffeineCache(n, Caffeine.from(spec).build(), false) {
|
return new CaffeineCache(n, Caffeine.from(spec).build(), false) {
|
||||||
|
|
||||||
@@ -188,35 +215,35 @@ public class TenantAwareCacheManager implements CacheManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Value
|
@Value
|
||||||
private static class Nop implements Cache {
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
private static class Nop extends AbstractValueAdaptingCache {
|
||||||
|
|
||||||
String name;
|
String name;
|
||||||
|
|
||||||
|
private Nop(final String name) {
|
||||||
|
super(false);
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
@Override
|
||||||
public Object getNativeCache() {
|
public @NonNull Object getNativeCache() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValueWrapper get(@NonNull final Object key) {
|
@SuppressWarnings("unchecked")
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T get(@NonNull final Object key, final Class<T> type) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T get(@NonNull final Object key, @NonNull final Callable<T> valueLoader) {
|
public <T> T get(@NonNull final Object key, @NonNull final Callable<T> valueLoader) {
|
||||||
return null;
|
try {
|
||||||
|
return (T) fromStoreValue(valueLoader.call());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ValueRetrievalException(key, valueLoader, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -233,5 +260,10 @@ public class TenantAwareCacheManager implements CacheManager {
|
|||||||
public void clear() {
|
public void clear() {
|
||||||
// nop
|
// nop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Object lookup(@NonNull final Object key) {
|
||||||
|
return null; // nop cache doesn't cache anything, especially used to DO NOT cache null values
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
* SPDX-License-Identifier: EPL-2.0
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
*/
|
*/
|
||||||
package org.eclipse.hawkbit.im.authentication;
|
package org.eclipse.hawkbit.auth;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
* Feature: Unit Tests - Security<br/>
|
* Feature: Unit Tests - Security<br/>
|
||||||
* Story: Permission Test
|
* Story: Permission Test
|
||||||
*/
|
*/
|
||||||
final class SpPermissionTest {
|
class SpPermissionTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Double-checks that all permissions doesn't contain any hierarchies.
|
* Double-checks that all permissions doesn't contain any hierarchies.
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Contributors to the Eclipse Foundation
|
||||||
|
*
|
||||||
|
* This program and the accompanying materials are made
|
||||||
|
* available under the terms of the Eclipse Public License 2.0
|
||||||
|
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: EPL-2.0
|
||||||
|
*/
|
||||||
|
package org.eclipse.hawkbit.tenancy;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class TenantAwareCacheManagerTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Double-checks that all permissions doesn't contain any hierarchies.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
void testNopCaches() {
|
||||||
|
assertThat(TenantAwareCacheManager.isNop(null)).isTrue();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop("")).isTrue();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop(" ")).isTrue();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop("\t")).isTrue();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop("nop")).isTrue();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop("none")).isTrue();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop("maximumSize=0")).isTrue();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop("maximumSize=0,something")).isTrue();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop("something, maximumSize=0")).isTrue();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop("something, maximumSize=0 \t, something")).isTrue();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop(" expireAfterWrite=0")).isTrue();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop("expireAfterWrite=0,something")).isTrue();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop("something, expireAfterWrite =0")).isTrue();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop("something, expireAfterWrite= 0 \t, something")).isTrue();
|
||||||
|
|
||||||
|
assertThat(TenantAwareCacheManager.isNop("maximumSize=01")).isFalse();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop("maximumSize=100")).isFalse();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop("expireAfterWrite=01")).isFalse();
|
||||||
|
assertThat(TenantAwareCacheManager.isNop("expireAfterWrite=100")).isFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,9 +23,9 @@ hawkbit.controller.maintenanceWindowPollCount=3
|
|||||||
|
|
||||||
# Cache config
|
# Cache config
|
||||||
hawkbit.cache.ttl=10s
|
hawkbit.cache.ttl=10s
|
||||||
hawkbit.cache.spec=expireAfterAccess=${hawkbit.cache.ttl}
|
hawkbit.cache.spec=expireAfterWrite=${hawkbit.cache.ttl}
|
||||||
hawkbit.cache.RolloutStatus.spec=maximumSize=50000,expireAfterAccess=${hawkbit.cache.ttl}
|
hawkbit.cache.RolloutStatus.spec=maximumSize=50000,expireAfterWrite=${hawkbit.cache.ttl}
|
||||||
hawkbit.cache.RolloutGroupStatus.spec=maximumSize=50000,expireAfterAccess=${hawkbit.cache.ttl}
|
hawkbit.cache.RolloutGroupStatus.spec=maximumSize=50000,expireAfterWrite=${hawkbit.cache.ttl}
|
||||||
# Cache config - END
|
# Cache config - END
|
||||||
|
|
||||||
# Attention: if you want to use a maximumPollingTime greater 23:59:59 you have to update the DurationField in the configuration window
|
# Attention: if you want to use a maximumPollingTime greater 23:59:59 you have to update the DurationField in the configuration window
|
||||||
|
|||||||
@@ -89,9 +89,6 @@ class TenantConfigurationManagementTest extends AbstractJpaIntegrationTest imple
|
|||||||
tenantConfigurationManagement.addOrUpdateConfiguration(
|
tenantConfigurationManagement.addOrUpdateConfiguration(
|
||||||
TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, newConfigurationValue2);
|
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
|
// verify that new configuration value is used
|
||||||
final TenantConfigurationValue<String> updatedConfigurationValue2 = tenantConfigurationManagement
|
final TenantConfigurationValue<String> updatedConfigurationValue2 = tenantConfigurationManagement
|
||||||
.getConfigurationValue(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, String.class);
|
.getConfigurationValue(TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY, String.class);
|
||||||
|
|||||||
Reference in New Issue
Block a user