From 40f6ae12010fb3913e742a281e77e763e6f13099 Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 11 Jan 2023 16:34:15 +0100 Subject: [PATCH 01/27] feat: bounded cache (variant 2) --- .../event/source/cache/BoundedCache.java | 18 ++++++ .../event/source/cache/BoundedItemStore.java | 46 ++++++++++++++ .../event/source/cache/BoundedStore.java | 52 ++++++++++++++++ .../cache/KubernetesResourceFetcher.java | 32 ++++++++++ .../event/source/cache/ResourceFetcher.java | 7 +++ .../event/source/cache/BoundedStoreTest.java | 61 +++++++++++++++++++ ...=> TemporaryResourceBoundedCacheTest.java} | 2 +- 7 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStore.java create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/ResourceFetcher.java create mode 100644 operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStoreTest.java rename operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/{TemporaryResourceCacheTest.java => TemporaryResourceBoundedCacheTest.java} (98%) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java new file mode 100644 index 0000000000..cfb8aff707 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java @@ -0,0 +1,18 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache; + +import java.util.Set; + +// todo: rename to cache? +public interface BoundedCache { + + R get(K key); + + R remove(K key); + + R put(K key, R object); + + Set keys(); + + Set values(); + +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java new file mode 100644 index 0000000000..a761edea0e --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java @@ -0,0 +1,46 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache; + +import java.util.HashSet; +import java.util.function.Function; +import java.util.stream.Stream; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.informers.cache.ItemStore; + +public class BoundedItemStore extends BoundedStore + implements ItemStore { + + private final Function keyFunction; + + public BoundedItemStore(KubernetesResourceFetcher resourceFetcher, + BoundedCache cache, Function keyFunction) { + super(resourceFetcher, cache); + this.keyFunction = keyFunction; + } + + @Override + public String getKey(R obj) { + return keyFunction.apply(obj); + } + + @Override + public Stream keySet() { + return super.keys(); + } + + /** This is very inefficient but should not be called by the Informer or just */ + @Override + public Stream values() { + var keys = cache.keys(); + var values = cache.values(); + var notPresentValueKeys = new HashSet<>(existingResources); + notPresentValueKeys.retainAll(keys); + var fetchedValues = notPresentValueKeys.stream().map(k -> fetchAndCacheResource(k)); + return Stream.concat(values.stream(), fetchedValues); + } + + @Override + public int size() { + return existingResources.size(); + } +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStore.java new file mode 100644 index 0000000000..f13cae5b3c --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStore.java @@ -0,0 +1,52 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Stream; + +public class BoundedStore { + + protected final ResourceFetcher resourceFetcher; + protected final BoundedCache cache; + protected Set existingResources = Collections.synchronizedSet(new HashSet<>());; + + public BoundedStore(ResourceFetcher resourceFetcher, BoundedCache cache) { + this.resourceFetcher = resourceFetcher; + this.cache = cache; + } + + public R get(K key) { + var res = cache.get(key); + if (res != null) { + return res; + } + if (!existingResources.contains(key)) { + return null; + } else { + return fetchAndCacheResource(key); + } + } + + public R remove(K key) { + existingResources.remove(key); + return cache.remove(key); + } + + public R put(K key, R object) { + var res = cache.put(key, object); + existingResources.add(key); + return res; + } + + public Stream keys() { + return existingResources.stream(); + } + + protected R fetchAndCacheResource(K key) { + var newRes = resourceFetcher.fetchResource(key); + cache.put(key, newRes); + return newRes; + } + +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java new file mode 100644 index 0000000000..5c799e1337 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java @@ -0,0 +1,32 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache; + +import java.util.function.Function; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.processing.event.ResourceID; + +public class KubernetesResourceFetcher + implements ResourceFetcher { + + private final Class rClass; + private final KubernetesClient client; + private final Function resourceIDFunction; + + public KubernetesResourceFetcher(Class rClass, + KubernetesClient client, + Function resourceIDFunction) { + this.rClass = rClass; + this.client = client; + this.resourceIDFunction = resourceIDFunction; + } + + @Override + public R fetchResource(String key) { + var resourceId = resourceIDFunction.apply(key); + return resourceId.getNamespace().map(ns -> client.resources(rClass).inNamespace(ns) + .withName(resourceId.getName()).get()) + .orElse(client.resources(rClass).withName(resourceId.getName()).get()); + + } +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/ResourceFetcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/ResourceFetcher.java new file mode 100644 index 0000000000..9cc4fe7a35 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/ResourceFetcher.java @@ -0,0 +1,7 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache; + +public interface ResourceFetcher { + + R fetchResource(K key); + +} diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStoreTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStoreTest.java new file mode 100644 index 0000000000..6045ec5794 --- /dev/null +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStoreTest.java @@ -0,0 +1,61 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache; + +import org.junit.jupiter.api.Test; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.processing.event.ResourceID; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +class BoundedStoreTest { + + ResourceFetcher resourceFetcher = mock(ResourceFetcher.class); + BoundedCache boundedCache = mock(BoundedCache.class); + + BoundedStore boundedStore = + new BoundedStore<>(resourceFetcher, boundedCache); + + @Test + void storesValue() { + boundedStore.put(ResourceID.fromResource(testValue1()), testValue1()); + + verify(boundedCache, times(1)).put(any(), any()); + } + + + @Test + void fetchesResourceIfNotPresentInCache() { + + } + + @Test + void removesValueFromCache() { + + } + + @Test + void listKeys() { + + } + + + ConfigMap testValue1() { + return testValue("test1"); + } + + ConfigMap testValue2() { + return testValue("test2"); + } + + ConfigMap testValue(String name) { + var cm = new ConfigMap(); + cm.setMetadata(new ObjectMetaBuilder() + .withName(name) + .withNamespace("default") + .build()); + return cm; + } +} diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceCacheTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceBoundedCacheTest.java similarity index 98% rename from operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceCacheTest.java rename to operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceBoundedCacheTest.java index f848e26cec..1599dc4a5f 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceCacheTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceBoundedCacheTest.java @@ -14,7 +14,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -class TemporaryResourceCacheTest { +class TemporaryResourceBoundedCacheTest { public static final String RESOURCE_VERSION = "1"; @SuppressWarnings("unchecked") From e267cd825c0fd6709abdea5e7d807d89fbe83fc4 Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 18 Jan 2023 16:10:27 +0100 Subject: [PATCH 02/27] wip --- .../event/source/cache/BoundedCache.java | 2 +- .../event/source/cache/BoundedItemStore.java | 15 +++++- .../cache/KubernetesResourceFetcher.java | 18 +++++++ .../cache/KubernetesResourceFetcherTest.java | 48 +++++++++++++++++++ 4 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcherTest.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java index cfb8aff707..39580b4dbe 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java @@ -2,7 +2,7 @@ import java.util.Set; -// todo: rename to cache? +// todo: rename to cache? (replace the old one) public interface BoundedCache { R get(K key); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java index a761edea0e..1fb88a45aa 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java @@ -5,6 +5,8 @@ import java.util.stream.Stream; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.informers.cache.Cache; import io.fabric8.kubernetes.client.informers.cache.ItemStore; public class BoundedItemStore extends BoundedStore @@ -12,6 +14,11 @@ public class BoundedItemStore extends BoundedStore keyFunction; + public BoundedItemStore(KubernetesClient client, Class resourceClass, + BoundedCache cache) { + this(new KubernetesResourceFetcher<>(resourceClass, client), cache, namespaceKeyFunc()); + } + public BoundedItemStore(KubernetesResourceFetcher resourceFetcher, BoundedCache cache, Function keyFunction) { super(resourceFetcher, cache); @@ -28,7 +35,9 @@ public Stream keySet() { return super.keys(); } - /** This is very inefficient but should not be called by the Informer or just */ + /** + * This is very inefficient but should not be called by the Informer or just before it's started + */ @Override public Stream values() { var keys = cache.keys(); @@ -43,4 +52,8 @@ public Stream values() { public int size() { return existingResources.size(); } + + public static Function namespaceKeyFunc() { + return r -> Cache.namespaceKeyFunc(r.getMetadata().getNamespace(), r.getMetadata().getName()); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java index 5c799e1337..6c4a68f947 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java @@ -13,6 +13,10 @@ public class KubernetesResourceFetcher private final KubernetesClient client; private final Function resourceIDFunction; + public KubernetesResourceFetcher(Class rClass, KubernetesClient client) { + this(rClass, client, inverseNamespaceKeyFunction()); + } + public KubernetesResourceFetcher(Class rClass, KubernetesClient client, Function resourceIDFunction) { @@ -27,6 +31,20 @@ public R fetchResource(String key) { return resourceId.getNamespace().map(ns -> client.resources(rClass).inNamespace(ns) .withName(resourceId.getName()).get()) .orElse(client.resources(rClass).withName(resourceId.getName()).get()); + } + /** + * This would not be needed + **/ + public static Function inverseNamespaceKeyFunction() { + return s -> { + int delimiterIndex = s.indexOf("/"); + if (delimiterIndex == -1) { + return new ResourceID(s); + } else { + return new ResourceID(s.substring(delimiterIndex + 1), s.substring(0, delimiterIndex)); + } + }; } + } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcherTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcherTest.java new file mode 100644 index 0000000000..1158e00295 --- /dev/null +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcherTest.java @@ -0,0 +1,48 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache; + +import org.junit.jupiter.api.Test; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition; + +import static org.assertj.core.api.Assertions.assertThat; + +class KubernetesResourceFetcherTest { + + public static final String DEFAULT_NAMESPACE = "default"; + public static final String TEST_RESOURCE_NAME = "test1"; + + @Test + void inverseKeyFunction() { + String key = BoundedItemStore.namespaceKeyFunc().apply(namespacedResource()); + var resourceID = KubernetesResourceFetcher.inverseNamespaceKeyFunction().apply(key); + + assertThat(resourceID.getNamespace()).isPresent().get().isEqualTo(DEFAULT_NAMESPACE); + assertThat(resourceID.getName()).isEqualTo(TEST_RESOURCE_NAME); + + key = BoundedItemStore.namespaceKeyFunc().apply(clusterScopedResource()); + resourceID = KubernetesResourceFetcher.inverseNamespaceKeyFunction().apply(key); + + assertThat(resourceID.getNamespace()).isEmpty(); + assertThat(resourceID.getName()).isEqualTo(TEST_RESOURCE_NAME); + } + + private HasMetadata namespacedResource() { + var cm = new ConfigMap(); + cm.setMetadata(new ObjectMetaBuilder() + .withName(TEST_RESOURCE_NAME) + .withNamespace(DEFAULT_NAMESPACE) + .build()); + return cm; + } + + private HasMetadata clusterScopedResource() { + var cm = new CustomResourceDefinition(); + cm.setMetadata(new ObjectMetaBuilder() + .withName(TEST_RESOURCE_NAME) + .build()); + return cm; + } +} From b0ca354a747e88caaddd59c6abf679bab7fc886d Mon Sep 17 00:00:00 2001 From: csviri Date: Thu, 19 Jan 2023 10:21:17 +0100 Subject: [PATCH 03/27] naming --- ...rceBoundedCacheTest.java => TemporaryResourceCacheTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/{TemporaryResourceBoundedCacheTest.java => TemporaryResourceCacheTest.java} (98%) diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceBoundedCacheTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceCacheTest.java similarity index 98% rename from operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceBoundedCacheTest.java rename to operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceCacheTest.java index 1599dc4a5f..f848e26cec 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceBoundedCacheTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceCacheTest.java @@ -14,7 +14,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -class TemporaryResourceBoundedCacheTest { +class TemporaryResourceCacheTest { public static final String RESOURCE_VERSION = "1"; @SuppressWarnings("unchecked") From 0b82aac23e0c93b23460a2eb15e08d40ef2ff41a Mon Sep 17 00:00:00 2001 From: csviri Date: Thu, 19 Jan 2023 12:55:02 +0100 Subject: [PATCH 04/27] added cache to configs --- .../api/config/BaseConfigurationService.java | 7 ++++++- .../ControllerConfigurationOverrider.java | 9 +++++++- .../config/DefaultResourceConfiguration.java | 10 ++++++++- .../ResolvedControllerConfiguration.java | 21 +++++++++++++------ .../api/config/ResourceConfiguration.java | 5 +++++ .../operator/api/config/Utils.java | 4 ++++ .../informer/InformerConfiguration.java | 15 ++++++++++--- .../reconciler/ControllerConfiguration.java | 3 +++ 8 files changed, 62 insertions(+), 12 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java index 2e8895ad87..3c1cbb3530 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java @@ -13,6 +13,7 @@ import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.informers.cache.ItemStore; import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.api.config.Utils.Configurator; @@ -133,6 +134,10 @@ protected

ControllerConfiguration

configFor(Reconcile timeUnit = reconciliationInterval.timeUnit(); } + final var itemStore = + Utils.instantiateAndConfigureIfNeeded(annotation.itemStore(), ItemStore.class, + Utils.contextFor(name), null); + final var config = new ResolvedControllerConfiguration

( resourceClass, name, generationAware, associatedReconcilerClass, retry, rateLimiter, @@ -152,7 +157,7 @@ protected

ControllerConfiguration

configFor(Reconcile valueOrDefault(annotation, io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::labelSelector, Constants.NO_VALUE_SET), - null); + null, itemStore); ResourceEventFilter

answer = deprecatedEventFilter(annotation); config.setEventFilter(answer != null ? answer : ResourceEventFilters.passthrough()); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java index 4a3efbc4a6..ab6b1d8e46 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java @@ -8,6 +8,7 @@ import java.util.Set; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.informers.cache.ItemStore; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter; import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter; @@ -36,6 +37,7 @@ public class ControllerConfigurationOverrider { private GenericFilter genericFilter; private RateLimiter rateLimiter; private Map configurations; + private ItemStore itemStore; private ControllerConfigurationOverrider(ControllerConfiguration original) { finalizer = original.getFinalizerName(); @@ -152,6 +154,11 @@ public ControllerConfigurationOverrider withGenericFilter(GenericFilter ge return this; } + public ControllerConfigurationOverrider withItemStore(ItemStore itemStore) { + this.itemStore = itemStore; + return this; + } + public ControllerConfigurationOverrider replacingNamedDependentResourceConfig(String name, Object dependentResourceConfig) { @@ -175,7 +182,7 @@ public ControllerConfiguration build() { generationAware, original.getAssociatedReconcilerClassName(), retry, rateLimiter, reconciliationMaxInterval, onAddFilter, onUpdateFilter, genericFilter, original.getDependentResources(), - namespaces, finalizer, labelSelector, configurations); + namespaces, finalizer, labelSelector, configurations, itemStore); overridden.setEventFilter(customResourcePredicate); return overridden; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultResourceConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultResourceConfiguration.java index b85bd76e9a..a612f2d136 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultResourceConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultResourceConfiguration.java @@ -4,6 +4,7 @@ import java.util.Set; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.informers.cache.ItemStore; import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter; import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter; @@ -19,10 +20,11 @@ public class DefaultResourceConfiguration private final GenericFilter genericFilter; private final String labelSelector; private final Set namespaces; + private final ItemStore itemStore; protected DefaultResourceConfiguration(Class resourceClass, Set namespaces, String labelSelector, OnAddFilter onAddFilter, - OnUpdateFilter onUpdateFilter, GenericFilter genericFilter) { + OnUpdateFilter onUpdateFilter, GenericFilter genericFilter, ItemStore itemStore) { this.resourceClass = resourceClass; this.resourceTypeName = ReconcilerUtils.getResourceTypeName(resourceClass); this.onAddFilter = onAddFilter; @@ -31,6 +33,7 @@ protected DefaultResourceConfiguration(Class resourceClass, this.namespaces = ResourceConfiguration.ensureValidNamespaces(namespaces); this.labelSelector = ResourceConfiguration.ensureValidLabelSelector(labelSelector); + this.itemStore = itemStore; } @Override @@ -66,4 +69,9 @@ public Optional> onUpdateFilter() { public Optional> genericFilter() { return Optional.ofNullable(genericFilter); } + + @Override + public Optional> getItemStore() { + return Optional.ofNullable(itemStore); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java index 5513b43060..844724fa48 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java @@ -5,6 +5,7 @@ import java.util.concurrent.TimeUnit; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.informers.cache.ItemStore; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceConfigurationProvider; import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; @@ -29,6 +30,7 @@ public class ResolvedControllerConfiguration

private final Duration maxReconciliationInterval; private final String finalizer; private final Map configurations; + private final ItemStore

itemStore; private ResourceEventFilter

eventFilter; private List dependentResources; @@ -40,7 +42,8 @@ public ResolvedControllerConfiguration(Class

resourceClass, ControllerConfigu other.onAddFilter().orElse(null), other.onUpdateFilter().orElse(null), other.genericFilter().orElse(null), other.getDependentResources(), other.getNamespaces(), - other.getFinalizerName(), other.getLabelSelector(), Collections.emptyMap()); + other.getFinalizerName(), other.getLabelSelector(), Collections.emptyMap(), + other.getItemStore().orElse(null)); } public static Duration getMaxReconciliationInterval(long interval, TimeUnit timeUnit) { @@ -67,10 +70,10 @@ public ResolvedControllerConfiguration(Class

resourceClass, String name, GenericFilter

genericFilter, List dependentResources, Set namespaces, String finalizer, String labelSelector, - Map configurations) { + Map configurations, ItemStore

itemStore) { this(resourceClass, name, generationAware, associatedReconcilerClassName, retry, rateLimiter, maxReconciliationInterval, onAddFilter, onUpdateFilter, genericFilter, - namespaces, finalizer, labelSelector, configurations); + namespaces, finalizer, labelSelector, configurations, itemStore); setDependentResources(dependentResources); } @@ -79,8 +82,9 @@ protected ResolvedControllerConfiguration(Class

resourceClass, String name, RateLimiter rateLimiter, Duration maxReconciliationInterval, OnAddFilter

onAddFilter, OnUpdateFilter

onUpdateFilter, GenericFilter

genericFilter, Set namespaces, String finalizer, String labelSelector, - Map configurations) { - super(resourceClass, namespaces, labelSelector, onAddFilter, onUpdateFilter, genericFilter); + Map configurations, ItemStore

itemStore) { + super(resourceClass, namespaces, labelSelector, onAddFilter, onUpdateFilter, genericFilter, + itemStore); this.name = ControllerConfiguration.ensureValidName(name, associatedReconcilerClassName); this.generationAware = generationAware; this.associatedReconcilerClassName = associatedReconcilerClassName; @@ -88,7 +92,7 @@ protected ResolvedControllerConfiguration(Class

resourceClass, String name, this.rateLimiter = ensureRateLimiter(rateLimiter); this.maxReconciliationInterval = maxReconciliationInterval; this.configurations = configurations != null ? configurations : Collections.emptyMap(); - + this.itemStore = itemStore; this.finalizer = ControllerConfiguration.ensureValidFinalizerName(finalizer, getResourceTypeName()); } @@ -162,4 +166,9 @@ protected void setEventFilter(ResourceEventFilter

eventFilter) { public Object getConfigurationFor(DependentResourceSpec spec) { return configurations.get(spec); } + + @Override + public Optional> getItemStore() { + return Optional.ofNullable(itemStore); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java index 81b7fdf4a7..fba0e53972 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java @@ -6,6 +6,7 @@ import java.util.Set; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.informers.cache.ItemStore; import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.api.reconciler.Constants; @@ -122,4 +123,8 @@ default Set getEffectiveNamespaces() { } return targetNamespaces; } + + default Optional> getItemStore() { + return Optional.empty(); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java index bee5b96cbe..61c3c27b91 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java @@ -237,6 +237,10 @@ public static String contextFor(ControllerConfiguration controllerConfigurati return contextFor(controllerConfiguration.getName(), dependentType, configurationAnnotation); } + public static String contextFor(String reconcilerName) { + return contextFor(reconcilerName, null, null); + } + @SuppressWarnings("rawtypes") public static String contextFor(String reconcilerName, Class dependentType, diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerConfiguration.java index c6ee847cb0..cafce36213 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerConfiguration.java @@ -5,6 +5,7 @@ import java.util.Set; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.informers.cache.ItemStore; import io.javaoperatorsdk.operator.api.config.DefaultResourceConfiguration; import io.javaoperatorsdk.operator.api.config.ResourceConfiguration; import io.javaoperatorsdk.operator.api.config.Utils; @@ -38,8 +39,10 @@ protected DefaultInformerConfiguration(String labelSelector, OnAddFilter onAddFilter, OnUpdateFilter onUpdateFilter, OnDeleteFilter onDeleteFilter, - GenericFilter genericFilter) { - super(resourceClass, namespaces, labelSelector, onAddFilter, onUpdateFilter, genericFilter); + GenericFilter genericFilter, + ItemStore itemStore) { + super(resourceClass, namespaces, labelSelector, onAddFilter, onUpdateFilter, genericFilter, + itemStore); this.followControllerNamespaceChanges = followControllerNamespaceChanges; this.primaryToSecondaryMapper = primaryToSecondaryMapper; @@ -103,6 +106,7 @@ class InformerConfigurationBuilder { private OnDeleteFilter onDeleteFilter; private GenericFilter genericFilter; private boolean inheritControllerNamespacesOnChange = false; + private ItemStore itemStore; private InformerConfigurationBuilder(Class resourceClass) { this.resourceClass = resourceClass; @@ -203,12 +207,17 @@ public InformerConfigurationBuilder withGenericFilter(GenericFilter generi return this; } + public InformerConfigurationBuilder withItemStore(ItemStore itemStore) { + this.itemStore = itemStore; + return this; + } + public InformerConfiguration build() { return new DefaultInformerConfiguration<>(labelSelector, resourceClass, primaryToSecondaryMapper, secondaryToPrimaryMapper, namespaces, inheritControllerNamespacesOnChange, onAddFilter, onUpdateFilter, - onDeleteFilter, genericFilter); + onDeleteFilter, genericFilter, itemStore); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java index ec76adf89d..a4a4a78797 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java @@ -6,6 +6,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import io.fabric8.kubernetes.client.informers.cache.ItemStore; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.processing.event.rate.LinearRateLimiter; import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter; @@ -118,4 +119,6 @@ MaxReconciliationInterval maxReconciliationInterval() default @MaxReconciliation * accessible no-arg constructor. */ Class rateLimiter() default LinearRateLimiter.class; + + Class itemStore() default ItemStore.class; } From f2c83dbd0d5ea4c16c50ffcbb83ff4dc68178197 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 20 Jan 2023 08:56:20 +0100 Subject: [PATCH 05/27] using new item store --- .../processing/event/source/informer/InformerManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java index 0a6b8327c5..db4ce49076 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java @@ -127,6 +127,7 @@ private InformerWrapper createEventSource( FilterWatchListDeletable, Resource> filteredBySelectorClient, ResourceEventHandler eventHandler, String namespaceIdentifier) { var informer = filteredBySelectorClient.runnableInformer(0); + configuration.getItemStore().ifPresent(informer::itemStore); var source = new InformerWrapper<>(informer, namespaceIdentifier); source.addEventHandler(eventHandler); From 329e0719716928086dcb1e53718ee0b85bf136b9 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 10 Feb 2023 15:34:36 +0100 Subject: [PATCH 06/27] wip --- caffein-bounded-cache-support/pom.xml | 72 ++++++++++++++ .../source/cache/CaffeinBoundedCache.java | 43 +++++++++ .../source/cache/CaffeinBoundedCacheIT.java | 81 ++++++++++++++++ .../BoundedCacheTestCustomResource.java | 13 +++ .../sample/BoundedCacheTestReconciler.java | 93 +++++++++++++++++++ .../cache/sample/BoundedCacheTestSpec.java | 15 +++ .../cache/sample/BoundedCacheTestStatus.java | 6 ++ .../event/source/cache/BoundedItemStore.java | 3 +- .../event/source/cache/BoundedStore.java | 26 ++++-- pom.xml | 7 ++ 10 files changed, 351 insertions(+), 8 deletions(-) create mode 100644 caffein-bounded-cache-support/pom.xml create mode 100644 caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java create mode 100644 caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java create mode 100644 caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestCustomResource.java create mode 100644 caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestReconciler.java create mode 100644 caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestSpec.java create mode 100644 caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestStatus.java diff --git a/caffein-bounded-cache-support/pom.xml b/caffein-bounded-cache-support/pom.xml new file mode 100644 index 0000000000..038cc4e150 --- /dev/null +++ b/caffein-bounded-cache-support/pom.xml @@ -0,0 +1,72 @@ + + + + java-operator-sdk + io.javaoperatorsdk + 4.3.0-SNAPSHOT + + 4.0.0 + + caffein-bounded-cache-support + Operator SDK - Caffein Bounded Cache Support + + + 11 + 11 + + + + + io.javaoperatorsdk + operator-framework-core + + + com.github.ben-manes.caffeine + caffeine + + + io.javaoperatorsdk + operator-framework + test + + + io.javaoperatorsdk + operator-framework-junit-5 + ${project.version} + test + + + io.fabric8 + crd-generator-apt + test + + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + + default-compile + compile + + compile + + + + -proc:none + + + + + + + + + \ No newline at end of file diff --git a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java new file mode 100644 index 0000000000..7b6b8a92fb --- /dev/null +++ b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java @@ -0,0 +1,43 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache; + +import java.util.HashSet; +import java.util.Set; + +import com.github.benmanes.caffeine.cache.Cache; + +public class CaffeinBoundedCache implements BoundedCache { + + private Cache cache; + + public CaffeinBoundedCache(Cache cache) { + this.cache = cache; + } + + @Override + public R get(K key) { + return cache.getIfPresent(key); + } + + @Override + public R remove(K key) { + var value = cache.getIfPresent(key); + cache.invalidate(key); + return value; + } + + @Override + public R put(K key, R object) { + cache.put(key, object); + return object; + } + + @Override + public Set keys() { + return cache.asMap().keySet(); + } + + @Override + public Set values() { + return new HashSet<>(cache.asMap().values()); + } +} diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java new file mode 100644 index 0000000000..9dd06f2bc4 --- /dev/null +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java @@ -0,0 +1,81 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache; + +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.BoundedCacheTestCustomResource; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.BoundedCacheTestReconciler; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.BoundedCacheTestSpec; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + +import static io.javaoperatorsdk.operator.processing.event.source.cache.sample.BoundedCacheTestReconciler.DATA_KEY; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +class CaffeinBoundedCacheIT { + + public static final int NUMBER_OF_RESOURCE_TO_TEST = 3; + public static final String RESOURCE_NAME_PREFIX = "test-"; + public static final String INITIAL_DATA_PREFIX = "data-"; + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder().withReconciler(new BoundedCacheTestReconciler(), o -> { + Cache cache = Caffeine.newBuilder() + .expireAfterAccess(1, TimeUnit.MINUTES) + .maximumSize(1) + .build(); + BoundedItemStore boundedItemStore = + new BoundedItemStore<>(new KubernetesClientBuilder().build(), ConfigMap.class, + new CaffeinBoundedCache<>(cache)); + o.withItemStore(boundedItemStore); + }) + .build(); + + @Test + void reconciliationWorksWithLimitedCache() { + createTestResources(); + + await().untilAsserted(() -> { + assertConfigMapData(INITIAL_DATA_PREFIX); + }); + } + + void assertConfigMapData(String dataPrefix) { + IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { + assertConfigMap(i, dataPrefix); + }); + } + + private void assertConfigMap(int i, String prefix) { + var cm = extension.get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); + assertThat(cm).isNotNull(); + assertThat(cm.getData().get(DATA_KEY)).isEqualTo(prefix + i); + } + + private void createTestResources() { + IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { + extension.create(createTestResource(i)); + }); + } + + BoundedCacheTestCustomResource createTestResource(int index) { + var res = new BoundedCacheTestCustomResource(); + res.setMetadata(new ObjectMetaBuilder() + .withName(RESOURCE_NAME_PREFIX + index) + .build()); + res.setSpec(new BoundedCacheTestSpec()); + res.getSpec().setData(INITIAL_DATA_PREFIX + index); + return res; + } + +} diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestCustomResource.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestCustomResource.java new file mode 100644 index 0000000000..fbc753b90b --- /dev/null +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestCustomResource.java @@ -0,0 +1,13 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache.sample; + +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.ShortNames; +import io.fabric8.kubernetes.model.annotation.Version; + +@Group("sample.javaoperatorsdk") +@Version("v1") +@ShortNames("bct") +public class BoundedCacheTestCustomResource + extends CustomResource { +} diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestReconciler.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestReconciler.java new file mode 100644 index 0000000000..fd1a52b93c --- /dev/null +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestReconciler.java @@ -0,0 +1,93 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache.sample; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.junit.KubernetesClientAware; +import io.javaoperatorsdk.operator.processing.event.source.EventSource; +import io.javaoperatorsdk.operator.processing.event.source.cache.BoundedItemStore; +import io.javaoperatorsdk.operator.processing.event.source.cache.CaffeinBoundedCache; +import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + +@ControllerConfiguration +public class BoundedCacheTestReconciler implements Reconciler, + EventSourceInitializer, KubernetesClientAware { + + public static final String DATA_KEY = "dataKey"; + private KubernetesClient client; + + @Override + public UpdateControl reconcile( + BoundedCacheTestCustomResource resource, + Context context) { + var maybeConfigMap = context.getSecondaryResource(ConfigMap.class); + maybeConfigMap.ifPresentOrElse( + cm -> updateConfigMapIfNeeded(cm, resource), + () -> createConfigMap(resource)); + ensureStatus(resource); + return UpdateControl.patchStatus(resource); + } + + private void updateConfigMapIfNeeded(ConfigMap cm, BoundedCacheTestCustomResource resource) { + var data = cm.getData().get(DATA_KEY); + if (data == null || data.equals(resource.getSpec().getData())) { + cm.setData(Map.of(DATA_KEY, resource.getSpec().getData())); + client.configMaps().resource(cm).replace(); + } + } + + private void createConfigMap(BoundedCacheTestCustomResource resource) { + var cm = new ConfigMapBuilder() + .withMetadata(new ObjectMetaBuilder() + .withName(resource.getMetadata().getName()) + .withNamespace(resource.getMetadata().getNamespace()) + .build()) + .withData(Map.of(DATA_KEY, resource.getSpec().getData())) + .build(); + cm.addOwnerReference(resource); + client.configMaps().resource(cm).create(); + } + + @Override + public Map prepareEventSources( + EventSourceContext context) { + Cache cache = Caffeine.newBuilder() + .expireAfterAccess(1, TimeUnit.MINUTES) + .maximumSize(1) + .build(); + + BoundedItemStore boundedItemStore = new BoundedItemStore<>(client, ConfigMap.class, + new CaffeinBoundedCache<>(cache)); + + var es = new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) + .withItemStore(boundedItemStore) + .build(), context); + + return EventSourceInitializer.nameEventSources(es); + } + + private void ensureStatus(BoundedCacheTestCustomResource resource) { + if (resource.getStatus() == null) { + resource.setStatus(new BoundedCacheTestStatus()); + } + } + + @Override + public KubernetesClient getKubernetesClient() { + return client; + } + + @Override + public void setKubernetesClient(KubernetesClient kubernetesClient) { + this.client = kubernetesClient; + } +} diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestSpec.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestSpec.java new file mode 100644 index 0000000000..caf07647ea --- /dev/null +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestSpec.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache.sample; + +public class BoundedCacheTestSpec { + + private String data; + + public String getData() { + return data; + } + + public BoundedCacheTestSpec setData(String data) { + this.data = data; + return this; + } +} diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestStatus.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestStatus.java new file mode 100644 index 0000000000..68331fbbf2 --- /dev/null +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestStatus.java @@ -0,0 +1,6 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache.sample; + +import io.javaoperatorsdk.operator.api.ObservedGenerationAwareStatus; + +public class BoundedCacheTestStatus extends ObservedGenerationAwareStatus { +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java index 1fb88a45aa..1f2ad0c26b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java @@ -44,7 +44,8 @@ public Stream values() { var values = cache.values(); var notPresentValueKeys = new HashSet<>(existingResources); notPresentValueKeys.retainAll(keys); - var fetchedValues = notPresentValueKeys.stream().map(k -> fetchAndCacheResource(k)); + var fetchedValues = + notPresentValueKeys.stream().map(k -> fetchAndCacheResourceIfStillNonPresent(k)); return Stream.concat(values.stream(), fetchedValues); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStore.java index f13cae5b3c..3db96fa78b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStore.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStore.java @@ -24,16 +24,16 @@ public R get(K key) { if (!existingResources.contains(key)) { return null; } else { - return fetchAndCacheResource(key); + return fetchAndCacheResourceIfStillNonPresent(key); } } - public R remove(K key) { + public synchronized R remove(K key) { existingResources.remove(key); return cache.remove(key); } - public R put(K key, R object) { + public synchronized R put(K key, R object) { var res = cache.put(key, object); existingResources.add(key); return res; @@ -43,10 +43,22 @@ public Stream keys() { return existingResources.stream(); } - protected R fetchAndCacheResource(K key) { + protected R fetchAndCacheResourceIfStillNonPresent(K key) { var newRes = resourceFetcher.fetchResource(key); - cache.put(key, newRes); - return newRes; + // todo unit test + // Just want to put the fetched resource if there is still no resource published from different + // source. + // In case of informers actually multiple events might arrive, therefore non fetched resources + // should + // take always precedence. + synchronized (this) { + var actual = cache.get(key); + if (actual == null) { + cache.put(key, newRes); + return newRes; + } else { + return actual; + } + } } - } diff --git a/pom.xml b/pom.xml index 4c088ed87d..b3a413ecf1 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,7 @@ 1.0 1.8.0 4.10.0 + 3.1.3 @@ -81,6 +82,7 @@ operator-framework micrometer-support sample-operators + caffein-bounded-cache-support @@ -193,6 +195,11 @@ mockwebserver ${okhttp.version} + + com.github.ben-manes.caffeine + caffeine + ${caffein.version} + From 2a51e5a7125706f503277b48c16f2f64aec52e38 Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 13 Feb 2023 14:17:41 +0100 Subject: [PATCH 07/27] working bounded cache --- caffein-bounded-cache-support/pom.xml | 12 ++ .../source/cache/CaffeinBoundedCache.java | 10 -- .../source/cache/CaffeinBoundedItemStore.java | 4 + .../source/cache/CaffeinBoundedCacheIT.java | 8 +- .../BoundedCacheTestCustomResource.java | 3 +- .../sample/BoundedCacheTestReconciler.java | 10 +- .../src/test/resources/log4j2.xml | 13 ++ .../event/source/cache/BoundedCache.java | 6 - .../event/source/cache/BoundedItemStore.java | 117 +++++++++++++++--- .../event/source/cache/BoundedStore.java | 64 ---------- .../event/source/cache/BoundedStoreTest.java | 61 --------- 11 files changed, 140 insertions(+), 168 deletions(-) create mode 100644 caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStore.java create mode 100644 caffein-bounded-cache-support/src/test/resources/log4j2.xml delete mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStore.java delete mode 100644 operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStoreTest.java diff --git a/caffein-bounded-cache-support/pom.xml b/caffein-bounded-cache-support/pom.xml index 038cc4e150..1f8bebd0bb 100644 --- a/caffein-bounded-cache-support/pom.xml +++ b/caffein-bounded-cache-support/pom.xml @@ -42,6 +42,18 @@ crd-generator-apt test + + org.apache.logging.log4j + log4j-slf4j-impl + test + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + test-jar + test + diff --git a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java index 7b6b8a92fb..1d601d6408 100644 --- a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java +++ b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java @@ -30,14 +30,4 @@ public R put(K key, R object) { cache.put(key, object); return object; } - - @Override - public Set keys() { - return cache.asMap().keySet(); - } - - @Override - public Set values() { - return new HashSet<>(cache.asMap().values()); - } } diff --git a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStore.java b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStore.java new file mode 100644 index 0000000000..69ff8f2039 --- /dev/null +++ b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStore.java @@ -0,0 +1,4 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache; + +public class CaffeinBoundedItemStore { +} diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java index 9dd06f2bc4..a4cc08028b 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java @@ -30,13 +30,13 @@ class CaffeinBoundedCacheIT { @RegisterExtension LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder().withReconciler(new BoundedCacheTestReconciler(), o -> { - Cache cache = Caffeine.newBuilder() + Cache cache = Caffeine.newBuilder() .expireAfterAccess(1, TimeUnit.MINUTES) .maximumSize(1) .build(); - BoundedItemStore boundedItemStore = - new BoundedItemStore<>(new KubernetesClientBuilder().build(), ConfigMap.class, - new CaffeinBoundedCache<>(cache)); + BoundedItemStore boundedItemStore = + new BoundedItemStore<>(new KubernetesClientBuilder().build(), + new CaffeinBoundedCache<>(cache), BoundedCacheTestCustomResource.class); o.withItemStore(boundedItemStore); }) .build(); diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestCustomResource.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestCustomResource.java index fbc753b90b..5c8c59c767 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestCustomResource.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestCustomResource.java @@ -1,5 +1,6 @@ package io.javaoperatorsdk.operator.processing.event.source.cache.sample; +import io.fabric8.kubernetes.api.model.Namespaced; import io.fabric8.kubernetes.client.CustomResource; import io.fabric8.kubernetes.model.annotation.Group; import io.fabric8.kubernetes.model.annotation.ShortNames; @@ -9,5 +10,5 @@ @Version("v1") @ShortNames("bct") public class BoundedCacheTestCustomResource - extends CustomResource { + extends CustomResource implements Namespaced { } diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestReconciler.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestReconciler.java index fd1a52b93c..2b2ed735cc 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestReconciler.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestReconciler.java @@ -3,6 +3,9 @@ import java.util.Map; import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ConfigMapBuilder; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; @@ -22,6 +25,8 @@ public class BoundedCacheTestReconciler implements Reconciler, EventSourceInitializer, KubernetesClientAware { + private static final Logger log = LoggerFactory.getLogger(BoundedCacheTestReconciler.class); + public static final String DATA_KEY = "dataKey"; private KubernetesClient client; @@ -34,6 +39,7 @@ public UpdateControl reconcile( cm -> updateConfigMapIfNeeded(cm, resource), () -> createConfigMap(resource)); ensureStatus(resource); + log.info("Reconciled: {}", resource.getMetadata().getName()); return UpdateControl.patchStatus(resource); } @@ -65,8 +71,8 @@ public Map prepareEventSources( .maximumSize(1) .build(); - BoundedItemStore boundedItemStore = new BoundedItemStore<>(client, ConfigMap.class, - new CaffeinBoundedCache<>(cache)); + BoundedItemStore boundedItemStore = new BoundedItemStore<>(client, + new CaffeinBoundedCache<>(cache), ConfigMap.class); var es = new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) .withItemStore(boundedItemStore) diff --git a/caffein-bounded-cache-support/src/test/resources/log4j2.xml b/caffein-bounded-cache-support/src/test/resources/log4j2.xml new file mode 100644 index 0000000000..f23cf772dd --- /dev/null +++ b/caffein-bounded-cache-support/src/test/resources/log4j2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java index 39580b4dbe..780fa1de9b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java @@ -1,7 +1,5 @@ package io.javaoperatorsdk.operator.processing.event.source.cache; -import java.util.Set; - // todo: rename to cache? (replace the old one) public interface BoundedCache { @@ -11,8 +9,4 @@ public interface BoundedCache { R put(K key, R object); - Set keys(); - - Set values(); - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java index 1f2ad0c26b..f59f48b893 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java @@ -1,28 +1,44 @@ package io.javaoperatorsdk.operator.processing.event.source.cache; -import java.util.HashSet; +import java.lang.reflect.InvocationTargetException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.stream.Stream; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.informers.cache.Cache; import io.fabric8.kubernetes.client.informers.cache.ItemStore; -public class BoundedItemStore extends BoundedStore +public class BoundedItemStore implements ItemStore { + private Logger log = LoggerFactory.getLogger(BoundedItemStore.class); + + private final ResourceFetcher resourceFetcher; + private final BoundedCache cache; private final Function keyFunction; + private final Map existingMinimalResources = new ConcurrentHashMap<>(); + private final Class resourceClass; - public BoundedItemStore(KubernetesClient client, Class resourceClass, - BoundedCache cache) { - this(new KubernetesResourceFetcher<>(resourceClass, client), cache, namespaceKeyFunc()); + public BoundedItemStore(KubernetesClient client, + BoundedCache cache, Class resourceClass) { + this(client, cache, resourceClass, namespaceKeyFunc()); } - public BoundedItemStore(KubernetesResourceFetcher resourceFetcher, - BoundedCache cache, Function keyFunction) { - super(resourceFetcher, cache); + public BoundedItemStore(KubernetesClient client, + BoundedCache cache, + Class resourceClass, + Function keyFunction) { + this.resourceFetcher = new KubernetesResourceFetcher<>(resourceClass, client); + this.cache = cache; this.keyFunction = keyFunction; + this.resourceClass = resourceClass; } @Override @@ -30,31 +46,92 @@ public String getKey(R obj) { return keyFunction.apply(obj); } + @Override + public synchronized R put(String key, R obj) { + var result = existingMinimalResources.get(key); + cache.put(key, obj); + existingMinimalResources.put(key, createMinimalResource(obj)); + return result; + } + + private R createMinimalResource(R obj) { + try { + R minimal = resourceClass.getConstructor().newInstance(); + minimal.setMetadata(new ObjectMetaBuilder().build()); + minimal.getMetadata().setName(obj.getMetadata().getName()); + minimal.getMetadata().setNamespace(obj.getMetadata().getNamespace()); + minimal.getMetadata().setResourceVersion(obj.getMetadata().getResourceVersion()); + return minimal; + } catch (InstantiationException | IllegalAccessException | InvocationTargetException + | NoSuchMethodException e) { + throw new IllegalStateException(e); + } + } + + @Override + public synchronized R remove(String key) { + var fullValue = cache.remove(key); + var minimalValue = existingMinimalResources.remove(key); + return fullValue != null ? fullValue : minimalValue; + } + @Override public Stream keySet() { - return super.keys(); + return existingMinimalResources.keySet().stream(); } - /** - * This is very inefficient but should not be called by the Informer or just before it's started - */ @Override public Stream values() { - var keys = cache.keys(); - var values = cache.values(); - var notPresentValueKeys = new HashSet<>(existingResources); - notPresentValueKeys.retainAll(keys); - var fetchedValues = - notPresentValueKeys.stream().map(k -> fetchAndCacheResourceIfStillNonPresent(k)); - return Stream.concat(values.stream(), fetchedValues); + return existingMinimalResources.values().stream(); } @Override public int size() { - return existingResources.size(); + return existingMinimalResources.size(); + } + + @Override + public R get(String key) { + var res = cache.get(key); + if (res != null) { + return res; + } + if (!existingMinimalResources.containsKey(key)) { + return null; + } else { + return refreshMissingStateFromServer(key); + } + } + + @Override + public boolean isFullState() { + return false; } public static Function namespaceKeyFunc() { return r -> Cache.namespaceKeyFunc(r.getMetadata().getNamespace(), r.getMetadata().getName()); } + + protected R refreshMissingStateFromServer(String key) { + log.debug("Fetching resource from server"); + var newRes = resourceFetcher.fetchResource(key); + synchronized (this) { + log.debug("Fetched resource: {}", newRes); + if (newRes == null) { + existingMinimalResources.remove(key); + return null; + } + // Just want to put the fetched resource if there is still no resource published from + // different source. In case of informers actually multiple events might arrive, therefore non + // fetched resource should take always precedence. + var actual = cache.get(key); + if (actual == null) { + cache.put(key, newRes); + existingMinimalResources.put(key, createMinimalResource(newRes)); + return newRes; + } else { + return actual; + } + } + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStore.java deleted file mode 100644 index 3db96fa78b..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStore.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.javaoperatorsdk.operator.processing.event.source.cache; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Stream; - -public class BoundedStore { - - protected final ResourceFetcher resourceFetcher; - protected final BoundedCache cache; - protected Set existingResources = Collections.synchronizedSet(new HashSet<>());; - - public BoundedStore(ResourceFetcher resourceFetcher, BoundedCache cache) { - this.resourceFetcher = resourceFetcher; - this.cache = cache; - } - - public R get(K key) { - var res = cache.get(key); - if (res != null) { - return res; - } - if (!existingResources.contains(key)) { - return null; - } else { - return fetchAndCacheResourceIfStillNonPresent(key); - } - } - - public synchronized R remove(K key) { - existingResources.remove(key); - return cache.remove(key); - } - - public synchronized R put(K key, R object) { - var res = cache.put(key, object); - existingResources.add(key); - return res; - } - - public Stream keys() { - return existingResources.stream(); - } - - protected R fetchAndCacheResourceIfStillNonPresent(K key) { - var newRes = resourceFetcher.fetchResource(key); - // todo unit test - // Just want to put the fetched resource if there is still no resource published from different - // source. - // In case of informers actually multiple events might arrive, therefore non fetched resources - // should - // take always precedence. - synchronized (this) { - var actual = cache.get(key); - if (actual == null) { - cache.put(key, newRes); - return newRes; - } else { - return actual; - } - } - } -} diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStoreTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStoreTest.java deleted file mode 100644 index 6045ec5794..0000000000 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedStoreTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package io.javaoperatorsdk.operator.processing.event.source.cache; - -import org.junit.jupiter.api.Test; - -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.javaoperatorsdk.operator.processing.event.ResourceID; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -class BoundedStoreTest { - - ResourceFetcher resourceFetcher = mock(ResourceFetcher.class); - BoundedCache boundedCache = mock(BoundedCache.class); - - BoundedStore boundedStore = - new BoundedStore<>(resourceFetcher, boundedCache); - - @Test - void storesValue() { - boundedStore.put(ResourceID.fromResource(testValue1()), testValue1()); - - verify(boundedCache, times(1)).put(any(), any()); - } - - - @Test - void fetchesResourceIfNotPresentInCache() { - - } - - @Test - void removesValueFromCache() { - - } - - @Test - void listKeys() { - - } - - - ConfigMap testValue1() { - return testValue("test1"); - } - - ConfigMap testValue2() { - return testValue("test2"); - } - - ConfigMap testValue(String name) { - var cm = new ConfigMap(); - cm.setMetadata(new ObjectMetaBuilder() - .withName(name) - .withNamespace("default") - .build()); - return cm; - } -} From 0a3b03576ab269686f80da4789952376d3cc1051 Mon Sep 17 00:00:00 2001 From: csviri Date: Mon, 13 Feb 2023 14:35:48 +0100 Subject: [PATCH 08/27] factory --- .../source/cache/CaffeinBoundedCache.java | 3 -- .../source/cache/CaffeinBoundedItemStore.java | 4 -- .../cache/CaffeinBoundedItemStores.java | 38 +++++++++++++++++++ .../source/cache/CaffeinBoundedCacheIT.java | 17 +++------ .../sample/BoundedCacheTestReconciler.java | 19 ++++------ 5 files changed, 50 insertions(+), 31 deletions(-) delete mode 100644 caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStore.java create mode 100644 caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java diff --git a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java index 1d601d6408..74dd194e57 100644 --- a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java +++ b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java @@ -1,8 +1,5 @@ package io.javaoperatorsdk.operator.processing.event.source.cache; -import java.util.HashSet; -import java.util.Set; - import com.github.benmanes.caffeine.cache.Cache; public class CaffeinBoundedCache implements BoundedCache { diff --git a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStore.java b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStore.java deleted file mode 100644 index 69ff8f2039..0000000000 --- a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStore.java +++ /dev/null @@ -1,4 +0,0 @@ -package io.javaoperatorsdk.operator.processing.event.source.cache; - -public class CaffeinBoundedItemStore { -} diff --git a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java new file mode 100644 index 0000000000..a3bd1440f1 --- /dev/null +++ b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java @@ -0,0 +1,38 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache; + +import java.time.Duration; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.KubernetesClient; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + +public class CaffeinBoundedItemStores { + + public static BoundedItemStore boundedItemStore( + KubernetesClient client, Class rClass, + Duration accessExpireDuration, long cacheMaxSize) { + Cache cache = Caffeine.newBuilder() + .expireAfterAccess(accessExpireDuration) + .maximumSize(cacheMaxSize) + .build(); + return boundedItemStore(client, rClass, cache); + } + + public static BoundedItemStore boundedItemStore( + KubernetesClient client, Class rClass, + Duration accessExpireDuration) { + Cache cache = Caffeine.newBuilder() + .expireAfterAccess(accessExpireDuration) + .build(); + return boundedItemStore(client, rClass, cache); + } + + public static BoundedItemStore boundedItemStore( + KubernetesClient client, Class rClass, Cache cache) { + return new BoundedItemStore<>(client, + new CaffeinBoundedCache<>(cache), rClass); + } + +} diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java index a4cc08028b..d857707389 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java @@ -1,6 +1,6 @@ package io.javaoperatorsdk.operator.processing.event.source.cache; -import java.util.concurrent.TimeUnit; +import java.time.Duration; import java.util.stream.IntStream; import org.junit.jupiter.api.Test; @@ -14,9 +14,6 @@ import io.javaoperatorsdk.operator.processing.event.source.cache.sample.BoundedCacheTestReconciler; import io.javaoperatorsdk.operator.processing.event.source.cache.sample.BoundedCacheTestSpec; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; - import static io.javaoperatorsdk.operator.processing.event.source.cache.sample.BoundedCacheTestReconciler.DATA_KEY; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; @@ -30,14 +27,10 @@ class CaffeinBoundedCacheIT { @RegisterExtension LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder().withReconciler(new BoundedCacheTestReconciler(), o -> { - Cache cache = Caffeine.newBuilder() - .expireAfterAccess(1, TimeUnit.MINUTES) - .maximumSize(1) - .build(); - BoundedItemStore boundedItemStore = - new BoundedItemStore<>(new KubernetesClientBuilder().build(), - new CaffeinBoundedCache<>(cache), BoundedCacheTestCustomResource.class); - o.withItemStore(boundedItemStore); + o.withItemStore(CaffeinBoundedItemStores.boundedItemStore( + new KubernetesClientBuilder().build(), BoundedCacheTestCustomResource.class, + Duration.ofMinutes(1), + 1)); }) .build(); diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestReconciler.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestReconciler.java index 2b2ed735cc..2be311d461 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestReconciler.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestReconciler.java @@ -1,7 +1,7 @@ package io.javaoperatorsdk.operator.processing.event.source.cache.sample; +import java.time.Duration; import java.util.Map; -import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,17 +10,14 @@ import io.fabric8.kubernetes.api.model.ConfigMapBuilder; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.junit.KubernetesClientAware; import io.javaoperatorsdk.operator.processing.event.source.EventSource; -import io.javaoperatorsdk.operator.processing.event.source.cache.BoundedItemStore; -import io.javaoperatorsdk.operator.processing.event.source.cache.CaffeinBoundedCache; +import io.javaoperatorsdk.operator.processing.event.source.cache.CaffeinBoundedItemStores; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; -import com.github.benmanes.caffeine.cache.Cache; -import com.github.benmanes.caffeine.cache.Caffeine; - @ControllerConfiguration public class BoundedCacheTestReconciler implements Reconciler, EventSourceInitializer, KubernetesClientAware { @@ -66,13 +63,11 @@ private void createConfigMap(BoundedCacheTestCustomResource resource) { @Override public Map prepareEventSources( EventSourceContext context) { - Cache cache = Caffeine.newBuilder() - .expireAfterAccess(1, TimeUnit.MINUTES) - .maximumSize(1) - .build(); - BoundedItemStore boundedItemStore = new BoundedItemStore<>(client, - new CaffeinBoundedCache<>(cache), ConfigMap.class); + var boundedItemStore = + CaffeinBoundedItemStores.boundedItemStore(new KubernetesClientBuilder().build(), + ConfigMap.class, Duration.ofMinutes(1), + 1); var es = new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) .withItemStore(boundedItemStore) From 19718f9b80a29f8696521fd84f445523f249d1ce Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 14 Feb 2023 09:43:51 +0100 Subject: [PATCH 09/27] full crud test --- .../source/cache/CaffeinBoundedCache.java | 3 +- .../cache/CaffeinBoundedItemStores.java | 2 + .../source/cache/CaffeinBoundedCacheIT.java | 36 +++++- .../event/source/cache/BoundedCache.java | 3 +- .../event/source/cache/BoundedItemStore.java | 11 +- .../source/cache/BoundedItemStoreTest.java | 104 ++++++++++++++++++ 6 files changed, 146 insertions(+), 13 deletions(-) create mode 100644 operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStoreTest.java diff --git a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java index 74dd194e57..b422f58001 100644 --- a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java +++ b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java @@ -23,8 +23,7 @@ public R remove(K key) { } @Override - public R put(K key, R object) { + public void put(K key, R object) { cache.put(key, object); - return object; } } diff --git a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java index a3bd1440f1..6bea3a40d1 100644 --- a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java +++ b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java @@ -10,6 +10,8 @@ public class CaffeinBoundedItemStores { + private CaffeinBoundedItemStores() {} + public static BoundedItemStore boundedItemStore( KubernetesClient client, Class rClass, Duration accessExpireDuration, long cacheMaxSize) { diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java index d857707389..8c7a193977 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java @@ -23,6 +23,7 @@ class CaffeinBoundedCacheIT { public static final int NUMBER_OF_RESOURCE_TO_TEST = 3; public static final String RESOURCE_NAME_PREFIX = "test-"; public static final String INITIAL_DATA_PREFIX = "data-"; + public static final String UPDATED_PREFIX = "updatedPrefix"; @RegisterExtension LocallyRunOperatorExtension extension = @@ -38,17 +39,44 @@ class CaffeinBoundedCacheIT { void reconciliationWorksWithLimitedCache() { createTestResources(); - await().untilAsserted(() -> { - assertConfigMapData(INITIAL_DATA_PREFIX); + assertConfigMapData(INITIAL_DATA_PREFIX); + + updateTestResources(); + + assertConfigMapData(UPDATED_PREFIX); + + deleteTestResources(); + + assertConfigMapsDeleted(); + } + + private void assertConfigMapsDeleted() { + await().untilAsserted(() -> IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { + var cm = extension.get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); + assertThat(cm).isNull(); + })); + } + + private void deleteTestResources() { + IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { + var cm = extension.get(BoundedCacheTestCustomResource.class, RESOURCE_NAME_PREFIX + i); + extension.delete(cm); }); } - void assertConfigMapData(String dataPrefix) { + private void updateTestResources() { IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { - assertConfigMap(i, dataPrefix); + var cm = extension.get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); + cm.getData().put(DATA_KEY, UPDATED_PREFIX + i); + extension.replace(cm); }); } + void assertConfigMapData(String dataPrefix) { + await().untilAsserted(() -> IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST) + .forEach(i -> assertConfigMap(i, dataPrefix))); + } + private void assertConfigMap(int i, String prefix) { var cm = extension.get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); assertThat(cm).isNotNull(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java index 780fa1de9b..1651d44dc7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java @@ -1,12 +1,11 @@ package io.javaoperatorsdk.operator.processing.event.source.cache; -// todo: rename to cache? (replace the old one) public interface BoundedCache { R get(K key); R remove(K key); - R put(K key, R object); + void put(K key, R object); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java index f59f48b893..1d2b886102 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java @@ -28,14 +28,15 @@ public class BoundedItemStore public BoundedItemStore(KubernetesClient client, BoundedCache cache, Class resourceClass) { - this(client, cache, resourceClass, namespaceKeyFunc()); + this(cache, resourceClass, namespaceKeyFunc(), + new KubernetesResourceFetcher<>(resourceClass, client)); } - public BoundedItemStore(KubernetesClient client, - BoundedCache cache, + public BoundedItemStore(BoundedCache cache, Class resourceClass, - Function keyFunction) { - this.resourceFetcher = new KubernetesResourceFetcher<>(resourceClass, client); + Function keyFunction, + ResourceFetcher resourceFetcher) { + this.resourceFetcher = resourceFetcher; this.cache = cache; this.keyFunction = keyFunction; this.resourceClass = resourceClass; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStoreTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStoreTest.java new file mode 100644 index 0000000000..88fb19c437 --- /dev/null +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStoreTest.java @@ -0,0 +1,104 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.informers.cache.Cache; +import io.javaoperatorsdk.operator.TestUtils; +import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; + +import static io.javaoperatorsdk.operator.processing.event.source.cache.BoundedItemStore.namespaceKeyFunc; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +class BoundedItemStoreTest { + + private BoundedItemStore boundedItemStore; + private BoundedCache boundedCache = mock(BoundedCache.class); + private ResourceFetcher resourceFetcher = mock(ResourceFetcher.class); + + @BeforeEach + void setup() { + boundedItemStore = new BoundedItemStore<>(boundedCache, + TestCustomResource.class, + namespaceKeyFunc(), + resourceFetcher); + } + + @Test + void notFetchesResourceFromServer() { + var res = boundedItemStore.get(testRes1Key()); + + assertThat(res).isNull(); + verify(resourceFetcher, never()).fetchResource(any()); + } + + @Test + void getsResourceFromServerIfNotInCache() { + boundedItemStore.put(testRes1Key(), + TestUtils.testCustomResource1()); + when(resourceFetcher.fetchResource(testRes1Key())) + .thenReturn(TestUtils.testCustomResource1()); + + var res = boundedItemStore.get(testRes1Key()); + + assertThat(res).isNotNull(); + verify(resourceFetcher, times(1)).fetchResource(any()); + } + + @Test + void ifFetchingNotFoundResourceRemovesItFromStore() { + boundedItemStore.put(testRes1Key(), + TestUtils.testCustomResource1()); + when(resourceFetcher.fetchResource(testRes1Key())) + .thenReturn(null); + + var res = boundedItemStore.get(testRes1Key()); + + assertThat(res).isNull(); + assertThat(boundedItemStore.keySet()).isEmpty(); + } + + @Test + void removesResourceFromCache() { + boundedItemStore.put(testRes1Key(), + TestUtils.testCustomResource1()); + + boundedItemStore.remove(testRes1Key()); + + var res = boundedItemStore.get(testRes1Key()); + verify(resourceFetcher, never()).fetchResource(any()); + assertThat(res).isNull(); + assertThat(boundedItemStore.keySet()).isEmpty(); + } + + @Test + void readingKeySeyNotReadsTheBoundedCache() { + boundedItemStore.put(testRes1Key(), + TestUtils.testCustomResource1()); + + boundedItemStore.keySet(); + + verify(boundedCache, never()).get(any()); + } + + @Test + void readingValuesNotReadsTheBoundedCache() { + boundedItemStore.put(testRes1Key(), + TestUtils.testCustomResource1()); + + boundedItemStore.values(); + + verify(boundedCache, never()).get(any()); + } + + String key(HasMetadata r) { + return Cache.namespaceKeyFunc(r.getMetadata().getNamespace(), r.getMetadata().getName()); + } + + String testRes1Key() { + return key(TestUtils.testCustomResource1()); + } +} From f490c0c414452593f9606a278f8129c40ddbff68 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 14 Feb 2023 11:16:16 +0100 Subject: [PATCH 10/27] cluster scope --- ...CacheIT.java => BoundedCacheTestBase.java} | 52 +++++--------- .../CaffeinBoundedCacheClusterScopeIT.java | 49 +++++++++++++ .../CaffeinBoundedCacheNamespacedIT.java | 49 +++++++++++++ ...ciler.java => AbstractTestReconciler.java} | 72 ++++++++++++------- ...edCacheClusterScopeTestCustomResource.java | 15 ++++ ...oundedCacheClusterScopeTestReconciler.java | 14 ++++ .../BoundedCacheTestCustomResource.java | 2 +- .../BoundedCacheTestReconciler.java | 14 ++++ .../BoundedCacheTestSpec.java | 2 +- .../BoundedCacheTestStatus.java | 2 +- 10 files changed, 208 insertions(+), 63 deletions(-) rename caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/{CaffeinBoundedCacheIT.java => BoundedCacheTestBase.java} (55%) create mode 100644 caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java create mode 100644 caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java rename caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/{BoundedCacheTestReconciler.java => AbstractTestReconciler.java} (57%) create mode 100644 caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestCustomResource.java create mode 100644 caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java rename caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/{ => namespacescope}/BoundedCacheTestCustomResource.java (95%) create mode 100644 caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestReconciler.java rename caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/{ => namespacescope}/BoundedCacheTestSpec.java (91%) rename caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/{ => namespacescope}/BoundedCacheTestStatus.java (89%) diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java similarity index 55% rename from caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java rename to caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java index 8c7a193977..008e5f7fc1 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheIT.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java @@ -1,40 +1,26 @@ package io.javaoperatorsdk.operator.processing.event.source.cache; -import java.time.Duration; import java.util.stream.IntStream; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; -import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.fabric8.kubernetes.client.CustomResource; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; -import io.javaoperatorsdk.operator.processing.event.source.cache.sample.BoundedCacheTestCustomResource; -import io.javaoperatorsdk.operator.processing.event.source.cache.sample.BoundedCacheTestReconciler; -import io.javaoperatorsdk.operator.processing.event.source.cache.sample.BoundedCacheTestSpec; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestSpec; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestStatus; -import static io.javaoperatorsdk.operator.processing.event.source.cache.sample.BoundedCacheTestReconciler.DATA_KEY; +import static io.javaoperatorsdk.operator.processing.event.source.cache.sample.AbstractTestReconciler.DATA_KEY; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -class CaffeinBoundedCacheIT { +public abstract class BoundedCacheTestBase

> { public static final int NUMBER_OF_RESOURCE_TO_TEST = 3; public static final String RESOURCE_NAME_PREFIX = "test-"; public static final String INITIAL_DATA_PREFIX = "data-"; public static final String UPDATED_PREFIX = "updatedPrefix"; - @RegisterExtension - LocallyRunOperatorExtension extension = - LocallyRunOperatorExtension.builder().withReconciler(new BoundedCacheTestReconciler(), o -> { - o.withItemStore(CaffeinBoundedItemStores.boundedItemStore( - new KubernetesClientBuilder().build(), BoundedCacheTestCustomResource.class, - Duration.ofMinutes(1), - 1)); - }) - .build(); - @Test void reconciliationWorksWithLimitedCache() { createTestResources(); @@ -52,23 +38,23 @@ void reconciliationWorksWithLimitedCache() { private void assertConfigMapsDeleted() { await().untilAsserted(() -> IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { - var cm = extension.get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); + var cm = extension().get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); assertThat(cm).isNull(); })); } private void deleteTestResources() { IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { - var cm = extension.get(BoundedCacheTestCustomResource.class, RESOURCE_NAME_PREFIX + i); - extension.delete(cm); + var cm = extension().get(customResourceClass(), RESOURCE_NAME_PREFIX + i); + extension().delete(cm); }); } private void updateTestResources() { IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { - var cm = extension.get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); + var cm = extension().get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); cm.getData().put(DATA_KEY, UPDATED_PREFIX + i); - extension.replace(cm); + extension().replace(cm); }); } @@ -78,25 +64,21 @@ void assertConfigMapData(String dataPrefix) { } private void assertConfigMap(int i, String prefix) { - var cm = extension.get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); + var cm = extension().get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); assertThat(cm).isNotNull(); assertThat(cm.getData().get(DATA_KEY)).isEqualTo(prefix + i); } private void createTestResources() { IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { - extension.create(createTestResource(i)); + extension().create(createTestResource(i)); }); } - BoundedCacheTestCustomResource createTestResource(int index) { - var res = new BoundedCacheTestCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(RESOURCE_NAME_PREFIX + index) - .build()); - res.setSpec(new BoundedCacheTestSpec()); - res.getSpec().setData(INITIAL_DATA_PREFIX + index); - return res; - } + abstract P createTestResource(int index); + + abstract Class

customResourceClass(); + + abstract LocallyRunOperatorExtension extension(); } diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java new file mode 100644 index 0000000000..ca4ff682ff --- /dev/null +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java @@ -0,0 +1,49 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache; + +import java.time.Duration; + +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.clusterscope.BoundedCacheClusterScopeTestCustomResource; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.clusterscope.BoundedCacheClusterScopeTestReconciler; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestSpec; + +public class CaffeinBoundedCacheClusterScopeIT + extends BoundedCacheTestBase { + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder() + .withReconciler(new BoundedCacheClusterScopeTestReconciler(), o -> { + o.withItemStore(CaffeinBoundedItemStores.boundedItemStore( + new KubernetesClientBuilder().build(), + BoundedCacheClusterScopeTestCustomResource.class, + Duration.ofMinutes(1), + 1)); + }) + .build(); + + @Override + BoundedCacheClusterScopeTestCustomResource createTestResource(int index) { + var res = new BoundedCacheClusterScopeTestCustomResource(); + res.setMetadata(new ObjectMetaBuilder() + .withName(RESOURCE_NAME_PREFIX + index) + .build()); + res.setSpec(new BoundedCacheTestSpec()); + res.getSpec().setData(INITIAL_DATA_PREFIX + index); + return res; + } + + @Override + Class customResourceClass() { + return BoundedCacheClusterScopeTestCustomResource.class; + } + + @Override + LocallyRunOperatorExtension extension() { + return extension; + } +} diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java new file mode 100644 index 0000000000..2a1b9152ec --- /dev/null +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java @@ -0,0 +1,49 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache; + +import java.time.Duration; + +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestCustomResource; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestReconciler; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestSpec; + +import static io.javaoperatorsdk.operator.processing.event.source.cache.BoundedCacheTestBase.INITIAL_DATA_PREFIX; +import static io.javaoperatorsdk.operator.processing.event.source.cache.BoundedCacheTestBase.RESOURCE_NAME_PREFIX; + +class CaffeinBoundedCacheNamespacedIT extends BoundedCacheTestBase { + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder().withReconciler(new BoundedCacheTestReconciler(), o -> { + o.withItemStore(CaffeinBoundedItemStores.boundedItemStore( + new KubernetesClientBuilder().build(), BoundedCacheTestCustomResource.class, + Duration.ofMinutes(1), + 1)); + }) + .build(); + + BoundedCacheTestCustomResource createTestResource(int index) { + var res = new BoundedCacheTestCustomResource(); + res.setMetadata(new ObjectMetaBuilder() + .withName(RESOURCE_NAME_PREFIX + index) + .build()); + res.setSpec(new BoundedCacheTestSpec()); + res.getSpec().setData(INITIAL_DATA_PREFIX + index); + return res; + } + + @Override + Class customResourceClass() { + return BoundedCacheTestCustomResource.class; + } + + @Override + LocallyRunOperatorExtension extension() { + return extension; + } + +} diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestReconciler.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java similarity index 57% rename from caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestReconciler.java rename to caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java index 2be311d461..184e3535fe 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestReconciler.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java @@ -6,31 +6,38 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.ConfigMapBuilder; -import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.api.model.*; +import io.fabric8.kubernetes.client.CustomResource; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.junit.KubernetesClientAware; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.cache.CaffeinBoundedItemStores; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.clusterscope.BoundedCacheClusterScopeTestReconciler; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestSpec; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestStatus; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; +import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers; -@ControllerConfiguration -public class BoundedCacheTestReconciler implements Reconciler, - EventSourceInitializer, KubernetesClientAware { +public abstract class AbstractTestReconciler

> + implements KubernetesClientAware, Reconciler

, + EventSourceInitializer

{ - private static final Logger log = LoggerFactory.getLogger(BoundedCacheTestReconciler.class); + private static final Logger log = + LoggerFactory.getLogger(BoundedCacheClusterScopeTestReconciler.class); public static final String DATA_KEY = "dataKey"; - private KubernetesClient client; + public static final String DEFAULT_NAMESPACE = "default"; + + protected KubernetesClient client; @Override - public UpdateControl reconcile( - BoundedCacheTestCustomResource resource, - Context context) { + public UpdateControl

reconcile( + P resource, + Context

context) { var maybeConfigMap = context.getSecondaryResource(ConfigMap.class); maybeConfigMap.ifPresentOrElse( cm -> updateConfigMapIfNeeded(cm, resource), @@ -40,7 +47,7 @@ public UpdateControl reconcile( return UpdateControl.patchStatus(resource); } - private void updateConfigMapIfNeeded(ConfigMap cm, BoundedCacheTestCustomResource resource) { + protected void updateConfigMapIfNeeded(ConfigMap cm, P resource) { var data = cm.getData().get(DATA_KEY); if (data == null || data.equals(resource.getSpec().getData())) { cm.setData(Map.of(DATA_KEY, resource.getSpec().getData())); @@ -48,21 +55,40 @@ private void updateConfigMapIfNeeded(ConfigMap cm, BoundedCacheTestCustomResourc } } - private void createConfigMap(BoundedCacheTestCustomResource resource) { + protected void createConfigMap(P resource) { var cm = new ConfigMapBuilder() .withMetadata(new ObjectMetaBuilder() .withName(resource.getMetadata().getName()) - .withNamespace(resource.getMetadata().getNamespace()) + .withNamespace(resource instanceof Namespaced ? resource.getMetadata().getNamespace() + : DEFAULT_NAMESPACE) .build()) .withData(Map.of(DATA_KEY, resource.getSpec().getData())) .build(); - cm.addOwnerReference(resource); + + if (resource instanceof Namespaced) { + cm.getMetadata() + .setLabels(Map.of(Mappers.DEFAULT_ANNOTATION_FOR_NAME, resource.getMetadata().getName())); + } else { + cm.addOwnerReference(resource); + } + + client.configMaps().resource(cm).create(); } + @Override + public KubernetesClient getKubernetesClient() { + return client; + } + + @Override + public void setKubernetesClient(KubernetesClient kubernetesClient) { + this.client = kubernetesClient; + } + @Override public Map prepareEventSources( - EventSourceContext context) { + EventSourceContext

context) { var boundedItemStore = CaffeinBoundedItemStores.boundedItemStore(new KubernetesClientBuilder().build(), @@ -70,25 +96,21 @@ public Map prepareEventSources( 1); var es = new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) + .withSecondaryToPrimaryMapper( + Namespaced.class.isAssignableFrom(customResourceClass()) ? Mappers.fromOwnerReference() + : Mappers.fromDefaultAnnotations()) .withItemStore(boundedItemStore) .build(), context); return EventSourceInitializer.nameEventSources(es); } - private void ensureStatus(BoundedCacheTestCustomResource resource) { + private void ensureStatus(P resource) { if (resource.getStatus() == null) { resource.setStatus(new BoundedCacheTestStatus()); } } - @Override - public KubernetesClient getKubernetesClient() { - return client; - } + protected abstract Class

customResourceClass(); - @Override - public void setKubernetesClient(KubernetesClient kubernetesClient) { - this.client = kubernetesClient; - } } diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestCustomResource.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestCustomResource.java new file mode 100644 index 0000000000..a77416715e --- /dev/null +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestCustomResource.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache.sample.clusterscope; + +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.ShortNames; +import io.fabric8.kubernetes.model.annotation.Version; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestSpec; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestStatus; + +@Group("sample.javaoperatorsdk") +@Version("v1") +@ShortNames("bccs") +public class BoundedCacheClusterScopeTestCustomResource + extends CustomResource { +} diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java new file mode 100644 index 0000000000..e7354a5c87 --- /dev/null +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java @@ -0,0 +1,14 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache.sample.clusterscope; + +import io.javaoperatorsdk.operator.api.reconciler.*; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.AbstractTestReconciler; + +@ControllerConfiguration +public class BoundedCacheClusterScopeTestReconciler extends + AbstractTestReconciler { + + @Override + protected Class customResourceClass() { + return BoundedCacheClusterScopeTestCustomResource.class; + } +} diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestCustomResource.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestCustomResource.java similarity index 95% rename from caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestCustomResource.java rename to caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestCustomResource.java index 5c8c59c767..a5e37917ba 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestCustomResource.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestCustomResource.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.processing.event.source.cache.sample; +package io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope; import io.fabric8.kubernetes.api.model.Namespaced; import io.fabric8.kubernetes.client.CustomResource; diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestReconciler.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestReconciler.java new file mode 100644 index 0000000000..e333b08668 --- /dev/null +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestReconciler.java @@ -0,0 +1,14 @@ +package io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope; + +import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; +import io.javaoperatorsdk.operator.processing.event.source.cache.sample.AbstractTestReconciler; + +@ControllerConfiguration +public class BoundedCacheTestReconciler + extends AbstractTestReconciler { + + @Override + protected Class customResourceClass() { + return BoundedCacheTestCustomResource.class; + } +} diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestSpec.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestSpec.java similarity index 91% rename from caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestSpec.java rename to caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestSpec.java index caf07647ea..b10aeb5ab4 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestSpec.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestSpec.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.processing.event.source.cache.sample; +package io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope; public class BoundedCacheTestSpec { diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestStatus.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestStatus.java similarity index 89% rename from caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestStatus.java rename to caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestStatus.java index 68331fbbf2..03a311529e 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/BoundedCacheTestStatus.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestStatus.java @@ -1,4 +1,4 @@ -package io.javaoperatorsdk.operator.processing.event.source.cache.sample; +package io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope; import io.javaoperatorsdk.operator.api.ObservedGenerationAwareStatus; From f072ab4de757adb01e2d4fe744aa11fe4253d949 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 14 Feb 2023 13:44:26 +0100 Subject: [PATCH 11/27] revert owner ref usage --- .../cache/sample/AbstractTestReconciler.java | 16 +--------------- .../BoundedCacheClusterScopeTestReconciler.java | 4 ---- .../BoundedCacheTestReconciler.java | 4 ---- 3 files changed, 1 insertion(+), 23 deletions(-) diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java index 184e3535fe..63c69c600e 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java @@ -20,7 +20,6 @@ import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestSpec; import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestStatus; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; -import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers; public abstract class AbstractTestReconciler

> implements KubernetesClientAware, Reconciler

, @@ -64,15 +63,7 @@ protected void createConfigMap(P resource) { .build()) .withData(Map.of(DATA_KEY, resource.getSpec().getData())) .build(); - - if (resource instanceof Namespaced) { - cm.getMetadata() - .setLabels(Map.of(Mappers.DEFAULT_ANNOTATION_FOR_NAME, resource.getMetadata().getName())); - } else { - cm.addOwnerReference(resource); - } - - + cm.addOwnerReference(resource); client.configMaps().resource(cm).create(); } @@ -96,9 +87,6 @@ public Map prepareEventSources( 1); var es = new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) - .withSecondaryToPrimaryMapper( - Namespaced.class.isAssignableFrom(customResourceClass()) ? Mappers.fromOwnerReference() - : Mappers.fromDefaultAnnotations()) .withItemStore(boundedItemStore) .build(), context); @@ -111,6 +99,4 @@ private void ensureStatus(P resource) { } } - protected abstract Class

customResourceClass(); - } diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java index e7354a5c87..a154659164 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java @@ -7,8 +7,4 @@ public class BoundedCacheClusterScopeTestReconciler extends AbstractTestReconciler { - @Override - protected Class customResourceClass() { - return BoundedCacheClusterScopeTestCustomResource.class; - } } diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestReconciler.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestReconciler.java index e333b08668..211877b361 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestReconciler.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestReconciler.java @@ -7,8 +7,4 @@ public class BoundedCacheTestReconciler extends AbstractTestReconciler { - @Override - protected Class customResourceClass() { - return BoundedCacheTestCustomResource.class; - } } From db6370466dd11e39a5d317b22e8124869c2f0432 Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 15 Feb 2023 10:06:57 +0100 Subject: [PATCH 12/27] fix IT --- .../cache/CaffeinBoundedCacheClusterScopeIT.java | 1 + .../source/cache/CaffeinBoundedCacheNamespacedIT.java | 4 +--- .../source/cache/sample/AbstractTestReconciler.java | 6 ++++-- .../sample/namespacescope/BoundedCacheTestSpec.java | 10 ++++++++++ 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java index ca4ff682ff..cfdb2ce35b 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java @@ -34,6 +34,7 @@ BoundedCacheClusterScopeTestCustomResource createTestResource(int index) { .build()); res.setSpec(new BoundedCacheTestSpec()); res.getSpec().setData(INITIAL_DATA_PREFIX + index); + res.getSpec().setTargetNamespace(extension.getNamespace()); return res; } diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java index 2a1b9152ec..ac63716ad0 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java @@ -11,9 +11,6 @@ import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestReconciler; import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestSpec; -import static io.javaoperatorsdk.operator.processing.event.source.cache.BoundedCacheTestBase.INITIAL_DATA_PREFIX; -import static io.javaoperatorsdk.operator.processing.event.source.cache.BoundedCacheTestBase.RESOURCE_NAME_PREFIX; - class CaffeinBoundedCacheNamespacedIT extends BoundedCacheTestBase { @RegisterExtension @@ -33,6 +30,7 @@ BoundedCacheTestCustomResource createTestResource(int index) { .build()); res.setSpec(new BoundedCacheTestSpec()); res.getSpec().setData(INITIAL_DATA_PREFIX + index); + res.getSpec().setTargetNamespace(extension.getNamespace()); return res; } diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java index 63c69c600e..f2e10f145a 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java @@ -20,6 +20,7 @@ import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestSpec; import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestStatus; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; +import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers; public abstract class AbstractTestReconciler

> implements KubernetesClientAware, Reconciler

, @@ -58,8 +59,7 @@ protected void createConfigMap(P resource) { var cm = new ConfigMapBuilder() .withMetadata(new ObjectMetaBuilder() .withName(resource.getMetadata().getName()) - .withNamespace(resource instanceof Namespaced ? resource.getMetadata().getNamespace() - : DEFAULT_NAMESPACE) + .withNamespace(resource.getSpec().getTargetNamespace()) .build()) .withData(Map.of(DATA_KEY, resource.getSpec().getData())) .build(); @@ -88,6 +88,8 @@ public Map prepareEventSources( var es = new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) .withItemStore(boundedItemStore) + .withSecondaryToPrimaryMapper( + Mappers.fromOwnerReference(this instanceof BoundedCacheClusterScopeTestReconciler)) .build(), context); return EventSourceInitializer.nameEventSources(es); diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestSpec.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestSpec.java index b10aeb5ab4..63e5876267 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestSpec.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestSpec.java @@ -3,6 +3,7 @@ public class BoundedCacheTestSpec { private String data; + private String targetNamespace; public String getData() { return data; @@ -12,4 +13,13 @@ public BoundedCacheTestSpec setData(String data) { this.data = data; return this; } + + public String getTargetNamespace() { + return targetNamespace; + } + + public BoundedCacheTestSpec setTargetNamespace(String targetNamespace) { + this.targetNamespace = targetNamespace; + return this; + } } From 75fbc38378648b2a2a9b3c66685f474912720902 Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 15 Feb 2023 11:24:34 +0100 Subject: [PATCH 13/27] wip --- .../event/source/cache/CaffeinBoundedCache.java | 3 +++ .../event/source/cache/BoundedCacheTestBase.java | 3 ++- .../operator/api/config/ResourceConfiguration.java | 11 +++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java index b422f58001..8d7749ba62 100644 --- a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java +++ b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java @@ -2,6 +2,9 @@ import com.github.benmanes.caffeine.cache.Cache; +/** + * Caffein cache wrapper to be used in a {@link BoundedItemStore} + * */ public class CaffeinBoundedCache implements BoundedCache { private Cache cache; diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java index 008e5f7fc1..36a16c5212 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java @@ -1,5 +1,6 @@ package io.javaoperatorsdk.operator.processing.event.source.cache; +import java.time.Duration; import java.util.stream.IntStream; import org.junit.jupiter.api.Test; @@ -37,7 +38,7 @@ void reconciliationWorksWithLimitedCache() { } private void assertConfigMapsDeleted() { - await().untilAsserted(() -> IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { + await().atMost(Duration.ofSeconds(20)).untilAsserted(() -> IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { var cm = extension().get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); assertThat(cm).isNull(); })); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java index fba0e53972..261a28b8b8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java @@ -124,6 +124,17 @@ default Set getEffectiveNamespaces() { return targetNamespaces; } + /** + * See related method + * in fabric8 client. + * + * The main goal, is to be able to use limited caches. + * + * See {@link io.javaoperatorsdk.operator.processing.event.source.cache.BoundedItemStore} and + * CaffeinBoundedCache + * + * @return Optional ItemStore implementation. If present this item store will be used inside the informers. + */ default Optional> getItemStore() { return Optional.empty(); } From 88171b8d2b9443924d7ae66901b7c3dacc42a19a Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 15 Feb 2023 11:34:01 +0100 Subject: [PATCH 14/27] format --- .../event/source/cache/CaffeinBoundedCache.java | 2 +- .../event/source/cache/BoundedCacheTestBase.java | 9 +++++---- .../operator/api/config/ResourceConfiguration.java | 14 +++++++++----- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java index 8d7749ba62..936d18385d 100644 --- a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java +++ b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java @@ -4,7 +4,7 @@ /** * Caffein cache wrapper to be used in a {@link BoundedItemStore} - * */ + */ public class CaffeinBoundedCache implements BoundedCache { private Cache cache; diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java index 36a16c5212..cd990da6a4 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java @@ -38,10 +38,11 @@ void reconciliationWorksWithLimitedCache() { } private void assertConfigMapsDeleted() { - await().atMost(Duration.ofSeconds(20)).untilAsserted(() -> IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { - var cm = extension().get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); - assertThat(cm).isNull(); - })); + await().atMost(Duration.ofSeconds(20)) + .untilAsserted(() -> IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { + var cm = extension().get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); + assertThat(cm).isNull(); + })); } private void deleteTestResources() { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java index 261a28b8b8..fe1caaa611 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java @@ -125,15 +125,19 @@ default Set getEffectiveNamespaces() { } /** - * See related method - * in fabric8 client. + * See related + * method in fabric8 client. * * The main goal, is to be able to use limited caches. * - * See {@link io.javaoperatorsdk.operator.processing.event.source.cache.BoundedItemStore} and - * CaffeinBoundedCache + * See {@link io.javaoperatorsdk.operator.processing.event.source.cache.BoundedItemStore} and + * + * CaffeinBoundedCache * - * @return Optional ItemStore implementation. If present this item store will be used inside the informers. + * @return Optional ItemStore implementation. If present this item store will be used inside the + * informers. */ default Optional> getItemStore() { return Optional.empty(); From fd4e73dde1f7413faccc0ff66340f1d631652f4c Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 15 Feb 2023 14:18:10 +0100 Subject: [PATCH 15/27] docs --- .../cache/CaffeinBoundedItemStores.java | 28 ++++++++++++------- .../source/cache/BoundedCacheTestBase.java | 2 ++ .../CaffeinBoundedCacheClusterScopeIT.java | 4 ++- .../CaffeinBoundedCacheNamespacedIT.java | 4 ++- .../cache/sample/AbstractTestReconciler.java | 20 +++++++++++-- docs/documentation/features.md | 26 +++++++++++++++++ .../event/source/cache/BoundedItemStore.java | 2 +- 7 files changed, 70 insertions(+), 16 deletions(-) diff --git a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java index 6bea3a40d1..d81b4c6793 100644 --- a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java +++ b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java @@ -8,20 +8,28 @@ import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; +/** + * The idea about CaffeinBoundedItemStore-s is that, caffeine will cache the resources which were + * recently used, and will evict resource, which are not used for a while. This is ideal from the + * perspective that on startup controllers reconcile all resources (this is why a maxSize not ideal) + * but after a while it can happen (well depending on the controller and domain) that only some + * resources are actually active, thus related events happen. So in case large amount of custom + * resources only the active once will remain in the cache. Note that if a resource is reconciled + * all the secondary resources are usually reconciled too, in that case all those resources are + * fetched and populated to the cache, and will remain there for some time, for a subsequent + * reconciliations. + */ public class CaffeinBoundedItemStores { private CaffeinBoundedItemStores() {} - public static BoundedItemStore boundedItemStore( - KubernetesClient client, Class rClass, - Duration accessExpireDuration, long cacheMaxSize) { - Cache cache = Caffeine.newBuilder() - .expireAfterAccess(accessExpireDuration) - .maximumSize(cacheMaxSize) - .build(); - return boundedItemStore(client, rClass, cache); - } - + /** + * @param client Kubernetes Client + * @param rClass resource class + * @param accessExpireDuration the duration after resources is evicted from cache if not accessed. + * @return the ItemStore implementation + * @param resource type + */ public static BoundedItemStore boundedItemStore( KubernetesClient client, Class rClass, Duration accessExpireDuration) { diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java index cd990da6a4..d73c1398dc 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java @@ -83,4 +83,6 @@ private void createTestResources() { abstract LocallyRunOperatorExtension extension(); + + } diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java index cfdb2ce35b..0746793d81 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java @@ -11,6 +11,8 @@ import io.javaoperatorsdk.operator.processing.event.source.cache.sample.clusterscope.BoundedCacheClusterScopeTestReconciler; import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestSpec; +import static io.javaoperatorsdk.operator.processing.event.source.cache.sample.AbstractTestReconciler.boundedItemStore; + public class CaffeinBoundedCacheClusterScopeIT extends BoundedCacheTestBase { @@ -18,7 +20,7 @@ public class CaffeinBoundedCacheClusterScopeIT LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder() .withReconciler(new BoundedCacheClusterScopeTestReconciler(), o -> { - o.withItemStore(CaffeinBoundedItemStores.boundedItemStore( + o.withItemStore(boundedItemStore( new KubernetesClientBuilder().build(), BoundedCacheClusterScopeTestCustomResource.class, Duration.ofMinutes(1), diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java index ac63716ad0..cdba046c63 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java @@ -11,12 +11,14 @@ import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestReconciler; import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestSpec; +import static io.javaoperatorsdk.operator.processing.event.source.cache.sample.AbstractTestReconciler.boundedItemStore; + class CaffeinBoundedCacheNamespacedIT extends BoundedCacheTestBase { @RegisterExtension LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder().withReconciler(new BoundedCacheTestReconciler(), o -> { - o.withItemStore(CaffeinBoundedItemStores.boundedItemStore( + o.withItemStore(boundedItemStore( new KubernetesClientBuilder().build(), BoundedCacheTestCustomResource.class, Duration.ofMinutes(1), 1)); diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java index f2e10f145a..ef9c0765a0 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java @@ -15,6 +15,7 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.junit.KubernetesClientAware; import io.javaoperatorsdk.operator.processing.event.source.EventSource; +import io.javaoperatorsdk.operator.processing.event.source.cache.BoundedItemStore; import io.javaoperatorsdk.operator.processing.event.source.cache.CaffeinBoundedItemStores; import io.javaoperatorsdk.operator.processing.event.source.cache.sample.clusterscope.BoundedCacheClusterScopeTestReconciler; import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestSpec; @@ -22,6 +23,9 @@ import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + public abstract class AbstractTestReconciler

> implements KubernetesClientAware, Reconciler

, EventSourceInitializer

{ @@ -82,9 +86,8 @@ public Map prepareEventSources( EventSourceContext

context) { var boundedItemStore = - CaffeinBoundedItemStores.boundedItemStore(new KubernetesClientBuilder().build(), - ConfigMap.class, Duration.ofMinutes(1), - 1); + boundedItemStore(new KubernetesClientBuilder().build(), + ConfigMap.class, Duration.ofMinutes(1), 1); // setting max size for testing purposes var es = new InformerEventSource<>(InformerConfiguration.from(ConfigMap.class, context) .withItemStore(boundedItemStore) @@ -101,4 +104,15 @@ private void ensureStatus(P resource) { } } + public static BoundedItemStore boundedItemStore( + KubernetesClient client, Class rClass, + Duration accessExpireDuration, + // max size is only for testing purposes + long cacheMaxSize) { + Cache cache = Caffeine.newBuilder() + .expireAfterAccess(accessExpireDuration) + .maximumSize(cacheMaxSize) + .build(); + return CaffeinBoundedItemStores.boundedItemStore(client, rClass, cache); + } } diff --git a/docs/documentation/features.md b/docs/documentation/features.md index afc010ec06..f0c20e5d67 100644 --- a/docs/documentation/features.md +++ b/docs/documentation/features.md @@ -739,3 +739,29 @@ with a `mycrs` plural form will result in 2 files: **NOTE:** > Quarkus users using the `quarkus-operator-sdk` extension do not need to add any extra dependency > to get their CRD generated as this is handled by the extension itself. + +## Optimizing Caches + +One of the ideas around operator pattern, is that all the relevant resources are cached, thus reconciliation is usually +very fast (especially if it does not need to update resources) since it's mostly working with in memory state. +However or large clusters, caching huge amount of primary and secondary resources might consume lots of memory. +There are some semi-experimental (experimental in terms that it works, but we need feedback from real production usage) +features to optimize memory usage of controllers. + +### Bounded Caches for Informers + +Limiting caches for informers - thus for Kubernetes resources, both controllers primary resource - is supported for now. +The idea with the implementation that is provided, is that resources are in the cache for a limited time. +So for use cases, when a resource is only frequently reconciled when it is created, and later no or +occasionally reconciled, will be evicted from the cache, since the resources are not accessed. +If a resource accessed in the future but not in the cache, the bounded cache implementation will fetch it from +the service if needed. +Note that on start of a controller all the resources are reconciled, for this reason explicitly setting a maximal +size of a cache might not be ideal. In other words it is desired to have all the resources in the cache at startup, +but not later if not accessed. + +See usage of the related implementation using Caffein cache in integration tests for [primary resource](https://github.com/java-operator-sdk/java-operator-sdk/blob/10e11e587447667ef0da1ddb29e0ba15fcd24ada/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java#L19-L19) and for an [informer](https://github.com/java-operator-sdk/java-operator-sdk/blob/10e11e587447667ef0da1ddb29e0ba15fcd24ada/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java#L84-L93). + +See also [CaffeinBoundedItemStores](https://github.com/java-operator-sdk/java-operator-sdk/blob/10e11e587447667ef0da1ddb29e0ba15fcd24ada/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java#L22-L22) + + diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java index 1d2b886102..aa4a179317 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java @@ -114,7 +114,7 @@ public static Function namespaceKeyFunc() { } protected R refreshMissingStateFromServer(String key) { - log.debug("Fetching resource from server"); + log.debug("Fetching resource from server for key: {}", key); var newRes = resourceFetcher.fetchResource(key); synchronized (this) { log.debug("Fetched resource: {}", newRes); From 63767e0b4312a45f7d86e7a117fd9bc1504550b9 Mon Sep 17 00:00:00 2001 From: csviri Date: Thu, 16 Feb 2023 10:06:06 +0100 Subject: [PATCH 16/27] log test issue --- .../event/source/cache/BoundedCacheTestBase.java | 11 +++++++++-- .../source/cache/sample/AbstractTestReconciler.java | 1 - 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java index d73c1398dc..21adf81cc0 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java @@ -4,6 +4,8 @@ import java.util.stream.IntStream; import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.client.CustomResource; @@ -17,6 +19,8 @@ public abstract class BoundedCacheTestBase

> { + private static final Logger log = LoggerFactory.getLogger(BoundedCacheTestBase.class); + public static final int NUMBER_OF_RESOURCE_TO_TEST = 3; public static final String RESOURCE_NAME_PREFIX = "test-"; public static final String INITIAL_DATA_PREFIX = "data-"; @@ -38,7 +42,7 @@ void reconciliationWorksWithLimitedCache() { } private void assertConfigMapsDeleted() { - await().atMost(Duration.ofSeconds(20)) + await().atMost(Duration.ofSeconds(30)) .untilAsserted(() -> IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { var cm = extension().get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); assertThat(cm).isNull(); @@ -48,7 +52,10 @@ private void assertConfigMapsDeleted() { private void deleteTestResources() { IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { var cm = extension().get(customResourceClass(), RESOURCE_NAME_PREFIX + i); - extension().delete(cm); + var deleted = extension().delete(cm); + if (!deleted) { + log.warn("Custom resource might not be deleted: {}", cm); + } }); } diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java index ef9c0765a0..07f2813477 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java +++ b/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java @@ -34,7 +34,6 @@ public abstract class AbstractTestReconciler

Date: Mon, 20 Feb 2023 17:43:39 +0100 Subject: [PATCH 17/27] refactor: avoid computing context several times --- .../api/config/BaseConfigurationService.java | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java index 3c1cbb3530..f7e24a3c8a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java @@ -117,14 +117,14 @@ protected

ControllerConfiguration

configFor(Reconcile final var associatedReconcilerClass = ResolvedControllerConfiguration.getAssociatedReconcilerClassName(reconciler.getClass()); + final var context = Utils.contextFor(name); final Class retryClass = annotation.retry(); final var retry = Utils.instantiateAndConfigureIfNeeded(retryClass, Retry.class, - Utils.contextFor(name, null, null), configuratorFor(Retry.class, reconciler)); + context, configuratorFor(Retry.class, reconciler)); final Class rateLimiterClass = annotation.rateLimiter(); final var rateLimiter = Utils.instantiateAndConfigureIfNeeded(rateLimiterClass, - RateLimiter.class, - Utils.contextFor(name, null, null), configuratorFor(RateLimiter.class, reconciler)); + RateLimiter.class, context, configuratorFor(RateLimiter.class, reconciler)); final var reconciliationInterval = annotation.maxReconciliationInterval(); long interval = -1; @@ -133,21 +133,14 @@ protected

ControllerConfiguration

configFor(Reconcile interval = reconciliationInterval.interval(); timeUnit = reconciliationInterval.timeUnit(); } - - final var itemStore = - Utils.instantiateAndConfigureIfNeeded(annotation.itemStore(), ItemStore.class, - Utils.contextFor(name), null); - + final var config = new ResolvedControllerConfiguration

( resourceClass, name, generationAware, associatedReconcilerClass, retry, rateLimiter, ResolvedControllerConfiguration.getMaxReconciliationInterval(interval, timeUnit), - Utils.instantiate(annotation.onAddFilter(), OnAddFilter.class, - Utils.contextFor(name, null, null)), - Utils.instantiate(annotation.onUpdateFilter(), OnUpdateFilter.class, - Utils.contextFor(name, null, null)), - Utils.instantiate(annotation.genericFilter(), GenericFilter.class, - Utils.contextFor(name, null, null)), + Utils.instantiate(annotation.onAddFilter(), OnAddFilter.class, context), + Utils.instantiate(annotation.onUpdateFilter(), OnUpdateFilter.class, context), + Utils.instantiate(annotation.genericFilter(), GenericFilter.class, context), Set.of(valueOrDefault(annotation, io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::namespaces, DEFAULT_NAMESPACES_SET.toArray(String[]::new))), @@ -157,7 +150,8 @@ protected

ControllerConfiguration

configFor(Reconcile valueOrDefault(annotation, io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::labelSelector, Constants.NO_VALUE_SET), - null, itemStore); + null, + Utils.instantiate(annotation.itemStore(), ItemStore.class, context)); ResourceEventFilter

answer = deprecatedEventFilter(annotation); config.setEventFilter(answer != null ? answer : ResourceEventFilters.passthrough()); From 9895a67c000a3952b341e5602b3e57ef6bf4e988 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Mon, 20 Feb 2023 17:59:18 +0100 Subject: [PATCH 18/27] fix: logger should be static and final --- .../processing/event/source/cache/BoundedItemStore.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java index aa4a179317..c388d145b8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java @@ -18,7 +18,7 @@ public class BoundedItemStore implements ItemStore { - private Logger log = LoggerFactory.getLogger(BoundedItemStore.class); + private static final Logger log = LoggerFactory.getLogger(BoundedItemStore.class); private final ResourceFetcher resourceFetcher; private final BoundedCache cache; From 45ce799627d8e1e27c6f6c8b504219a0b55118dc Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Mon, 20 Feb 2023 18:00:33 +0100 Subject: [PATCH 19/27] refactor: make constructors more coherent --- .../event/source/cache/CaffeinBoundedItemStores.java | 3 +-- .../operator/api/config/BaseConfigurationService.java | 2 +- .../processing/event/source/cache/BoundedItemStore.java | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java index d81b4c6793..0641c7d33f 100644 --- a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java +++ b/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java @@ -41,8 +41,7 @@ public static BoundedItemStore boundedItemStore( public static BoundedItemStore boundedItemStore( KubernetesClient client, Class rClass, Cache cache) { - return new BoundedItemStore<>(client, - new CaffeinBoundedCache<>(cache), rClass); + return new BoundedItemStore<>(new CaffeinBoundedCache<>(cache), rClass, client); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java index f7e24a3c8a..7d0653a825 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java @@ -133,7 +133,7 @@ protected

ControllerConfiguration

configFor(Reconcile interval = reconciliationInterval.interval(); timeUnit = reconciliationInterval.timeUnit(); } - + final var config = new ResolvedControllerConfiguration

( resourceClass, name, generationAware, associatedReconcilerClass, retry, rateLimiter, diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java index c388d145b8..32a1b52370 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java @@ -26,8 +26,8 @@ public class BoundedItemStore private final Map existingMinimalResources = new ConcurrentHashMap<>(); private final Class resourceClass; - public BoundedItemStore(KubernetesClient client, - BoundedCache cache, Class resourceClass) { + public BoundedItemStore(BoundedCache cache, Class resourceClass, + KubernetesClient client) { this(cache, resourceClass, namespaceKeyFunc(), new KubernetesResourceFetcher<>(resourceClass, client)); } From 71635335e47c1e0ee60cde40e03f6b6d2aa81815 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Mon, 20 Feb 2023 18:42:17 +0100 Subject: [PATCH 20/27] refactor: do not look up constructor on each call --- .../operator/api/config/Utils.java | 20 +++++++++++++----- .../event/source/cache/BoundedItemStore.java | 21 +++++++++++-------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java index 61c3c27b91..ec244f224c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java @@ -203,9 +203,7 @@ public static T instantiateAndConfigureIfNeeded(Class targetCla } try { - final Constructor constructor = targetClass.getDeclaredConstructor(); - constructor.setAccessible(true); - final var instance = constructor.newInstance(); + final var instance = getConstructor(targetClass).newInstance(); if (configurator != null) { configurator.configure(instance); @@ -213,13 +211,25 @@ public static T instantiateAndConfigureIfNeeded(Class targetCla return instance; } catch (InstantiationException | IllegalAccessException | InvocationTargetException - | NoSuchMethodException e) { + | IllegalStateException e) { throw new OperatorException("Couldn't instantiate " + expectedType.getSimpleName() + " '" - + targetClass.getName() + "': you need to provide an accessible no-arg constructor." + + targetClass.getName() + "'." + (context != null ? " Context: " + context : ""), e); } } + public static Constructor getConstructor(Class targetClass) { + final Constructor constructor; + try { + constructor = targetClass.getDeclaredConstructor(); + } catch (NoSuchMethodException e) { + throw new IllegalStateException( + "Couldn't find a no-arg constructor for " + targetClass.getName(), e); + } + constructor.setAccessible(true); + return constructor; + } + public static T instantiate(Class toInstantiate, Class expectedType, String context) { return instantiateAndConfigureIfNeeded(toInstantiate, expectedType, context, null); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java index 32a1b52370..4e29304173 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java @@ -1,5 +1,6 @@ package io.javaoperatorsdk.operator.processing.event.source.cache; +import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -14,6 +15,7 @@ import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.informers.cache.Cache; import io.fabric8.kubernetes.client.informers.cache.ItemStore; +import io.javaoperatorsdk.operator.api.config.Utils; public class BoundedItemStore implements ItemStore { @@ -24,7 +26,7 @@ public class BoundedItemStore private final BoundedCache cache; private final Function keyFunction; private final Map existingMinimalResources = new ConcurrentHashMap<>(); - private final Class resourceClass; + private final Constructor resourceConstructor; public BoundedItemStore(BoundedCache cache, Class resourceClass, KubernetesClient client) { @@ -39,7 +41,7 @@ public BoundedItemStore(BoundedCache cache, this.resourceFetcher = resourceFetcher; this.cache = cache; this.keyFunction = keyFunction; - this.resourceClass = resourceClass; + this.resourceConstructor = Utils.getConstructor(resourceClass); } @Override @@ -57,14 +59,15 @@ public synchronized R put(String key, R obj) { private R createMinimalResource(R obj) { try { - R minimal = resourceClass.getConstructor().newInstance(); - minimal.setMetadata(new ObjectMetaBuilder().build()); - minimal.getMetadata().setName(obj.getMetadata().getName()); - minimal.getMetadata().setNamespace(obj.getMetadata().getNamespace()); - minimal.getMetadata().setResourceVersion(obj.getMetadata().getResourceVersion()); + R minimal = resourceConstructor.newInstance(); + final var metadata = obj.getMetadata(); + minimal.setMetadata(new ObjectMetaBuilder() + .withName(metadata.getName()) + .withNamespace(metadata.getNamespace()) + .withResourceVersion(metadata.getResourceVersion()) + .build()); return minimal; - } catch (InstantiationException | IllegalAccessException | InvocationTargetException - | NoSuchMethodException e) { + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new IllegalStateException(e); } } From e21febf35e7fbd7c46c89eb46008eae1cf23cf35 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Mon, 20 Feb 2023 21:00:41 +0100 Subject: [PATCH 21/27] fix: rename methods more appropriately --- .../source/cache/BoundedItemStoreTest.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStoreTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStoreTest.java index 88fb19c437..9381aedd0d 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStoreTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStoreTest.java @@ -11,13 +11,20 @@ import static io.javaoperatorsdk.operator.processing.event.source.cache.BoundedItemStore.namespaceKeyFunc; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; class BoundedItemStoreTest { private BoundedItemStore boundedItemStore; - private BoundedCache boundedCache = mock(BoundedCache.class); - private ResourceFetcher resourceFetcher = mock(ResourceFetcher.class); + @SuppressWarnings("unchecked") + private final BoundedCache boundedCache = mock(BoundedCache.class); + @SuppressWarnings("unchecked") + private final ResourceFetcher resourceFetcher = + mock(ResourceFetcher.class); @BeforeEach void setup() { @@ -28,7 +35,7 @@ void setup() { } @Test - void notFetchesResourceFromServer() { + void shouldNotFetchResourcesFromServerIfNotKnown() { var res = boundedItemStore.get(testRes1Key()); assertThat(res).isNull(); @@ -49,7 +56,7 @@ void getsResourceFromServerIfNotInCache() { } @Test - void ifFetchingNotFoundResourceRemovesItFromStore() { + void removesResourcesNotFoundOnServerFromStore() { boundedItemStore.put(testRes1Key(), TestUtils.testCustomResource1()); when(resourceFetcher.fetchResource(testRes1Key())) @@ -75,7 +82,7 @@ void removesResourceFromCache() { } @Test - void readingKeySeyNotReadsTheBoundedCache() { + void readingKeySetDoesNotReadFromBoundedCache() { boundedItemStore.put(testRes1Key(), TestUtils.testCustomResource1()); @@ -85,7 +92,7 @@ void readingKeySeyNotReadsTheBoundedCache() { } @Test - void readingValuesNotReadsTheBoundedCache() { + void readingValuesDoesNotReadFromBoundedCache() { boundedItemStore.put(testRes1Key(), TestUtils.testCustomResource1()); From 092aa9799431e770e9b752aa73eb1cfb840818c5 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 21 Feb 2023 10:59:47 +0100 Subject: [PATCH 22/27] naming fixes --- .../pom.xml | 4 ++-- .../processing/event/source/cache/CaffeineBoundedCache.java | 4 ++-- .../event/source/cache/CaffeineBoundedItemStores.java | 6 +++--- .../processing/event/source/cache/BoundedCacheTestBase.java | 0 .../source/cache/CaffeineBoundedCacheClusterScopeIT.java | 2 +- .../source/cache/CaffeineBoundedCacheNamespacedIT.java | 3 ++- .../event/source/cache/sample/AbstractTestReconciler.java | 4 ++-- .../BoundedCacheClusterScopeTestCustomResource.java | 0 .../BoundedCacheClusterScopeTestReconciler.java | 0 .../namespacescope/BoundedCacheTestCustomResource.java | 0 .../sample/namespacescope/BoundedCacheTestReconciler.java | 0 .../cache/sample/namespacescope/BoundedCacheTestSpec.java | 0 .../cache/sample/namespacescope/BoundedCacheTestStatus.java | 0 .../src/test/resources/log4j2.xml | 0 pom.xml | 2 +- 15 files changed, 13 insertions(+), 12 deletions(-) rename {caffein-bounded-cache-support => caffeine-bounded-cache-support}/pom.xml (95%) rename caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java => caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCache.java (81%) rename caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java => caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedItemStores.java (91%) rename {caffein-bounded-cache-support => caffeine-bounded-cache-support}/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java (100%) rename caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java => caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheClusterScopeIT.java (97%) rename caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java => caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheNamespacedIT.java (94%) rename {caffein-bounded-cache-support => caffeine-bounded-cache-support}/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java (97%) rename {caffein-bounded-cache-support => caffeine-bounded-cache-support}/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestCustomResource.java (100%) rename {caffein-bounded-cache-support => caffeine-bounded-cache-support}/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java (100%) rename {caffein-bounded-cache-support => caffeine-bounded-cache-support}/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestCustomResource.java (100%) rename {caffein-bounded-cache-support => caffeine-bounded-cache-support}/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestReconciler.java (100%) rename {caffein-bounded-cache-support => caffeine-bounded-cache-support}/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestSpec.java (100%) rename {caffein-bounded-cache-support => caffeine-bounded-cache-support}/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestStatus.java (100%) rename {caffein-bounded-cache-support => caffeine-bounded-cache-support}/src/test/resources/log4j2.xml (100%) diff --git a/caffein-bounded-cache-support/pom.xml b/caffeine-bounded-cache-support/pom.xml similarity index 95% rename from caffein-bounded-cache-support/pom.xml rename to caffeine-bounded-cache-support/pom.xml index 1f8bebd0bb..23b5334287 100644 --- a/caffein-bounded-cache-support/pom.xml +++ b/caffeine-bounded-cache-support/pom.xml @@ -9,8 +9,8 @@ 4.0.0 - caffein-bounded-cache-support - Operator SDK - Caffein Bounded Cache Support + caffeine-bounded-cache-support + Operator SDK - Caffeine Bounded Cache Support 11 diff --git a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java b/caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCache.java similarity index 81% rename from caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java rename to caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCache.java index 936d18385d..eb70fe04d3 100644 --- a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCache.java +++ b/caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCache.java @@ -5,11 +5,11 @@ /** * Caffein cache wrapper to be used in a {@link BoundedItemStore} */ -public class CaffeinBoundedCache implements BoundedCache { +public class CaffeineBoundedCache implements BoundedCache { private Cache cache; - public CaffeinBoundedCache(Cache cache) { + public CaffeineBoundedCache(Cache cache) { this.cache = cache; } diff --git a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java b/caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedItemStores.java similarity index 91% rename from caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java rename to caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedItemStores.java index 0641c7d33f..aa4db53030 100644 --- a/caffein-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedItemStores.java +++ b/caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedItemStores.java @@ -19,9 +19,9 @@ * fetched and populated to the cache, and will remain there for some time, for a subsequent * reconciliations. */ -public class CaffeinBoundedItemStores { +public class CaffeineBoundedItemStores { - private CaffeinBoundedItemStores() {} + private CaffeineBoundedItemStores() {} /** * @param client Kubernetes Client @@ -41,7 +41,7 @@ public static BoundedItemStore boundedItemStore( public static BoundedItemStore boundedItemStore( KubernetesClient client, Class rClass, Cache cache) { - return new BoundedItemStore<>(new CaffeinBoundedCache<>(cache), rClass, client); + return new BoundedItemStore<>(new CaffeineBoundedCache<>(cache), rClass, client); } } diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java similarity index 100% rename from caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java rename to caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheClusterScopeIT.java similarity index 97% rename from caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java rename to caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheClusterScopeIT.java index 0746793d81..252b20f4a4 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheClusterScopeIT.java +++ b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheClusterScopeIT.java @@ -13,7 +13,7 @@ import static io.javaoperatorsdk.operator.processing.event.source.cache.sample.AbstractTestReconciler.boundedItemStore; -public class CaffeinBoundedCacheClusterScopeIT +public class CaffeineBoundedCacheClusterScopeIT extends BoundedCacheTestBase { @RegisterExtension diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheNamespacedIT.java similarity index 94% rename from caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java rename to caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheNamespacedIT.java index cdba046c63..ae7f8f5873 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeinBoundedCacheNamespacedIT.java +++ b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheNamespacedIT.java @@ -13,7 +13,8 @@ import static io.javaoperatorsdk.operator.processing.event.source.cache.sample.AbstractTestReconciler.boundedItemStore; -class CaffeinBoundedCacheNamespacedIT extends BoundedCacheTestBase { +class CaffeineBoundedCacheNamespacedIT + extends BoundedCacheTestBase { @RegisterExtension LocallyRunOperatorExtension extension = diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java similarity index 97% rename from caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java rename to caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java index 07f2813477..835fcef91a 100644 --- a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java +++ b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java @@ -16,7 +16,7 @@ import io.javaoperatorsdk.operator.junit.KubernetesClientAware; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.cache.BoundedItemStore; -import io.javaoperatorsdk.operator.processing.event.source.cache.CaffeinBoundedItemStores; +import io.javaoperatorsdk.operator.processing.event.source.cache.CaffeineBoundedItemStores; import io.javaoperatorsdk.operator.processing.event.source.cache.sample.clusterscope.BoundedCacheClusterScopeTestReconciler; import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestSpec; import io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope.BoundedCacheTestStatus; @@ -112,6 +112,6 @@ public static BoundedItemStore boundedItemStore( .expireAfterAccess(accessExpireDuration) .maximumSize(cacheMaxSize) .build(); - return CaffeinBoundedItemStores.boundedItemStore(client, rClass, cache); + return CaffeineBoundedItemStores.boundedItemStore(client, rClass, cache); } } diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestCustomResource.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestCustomResource.java similarity index 100% rename from caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestCustomResource.java rename to caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestCustomResource.java diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java similarity index 100% rename from caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java rename to caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestCustomResource.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestCustomResource.java similarity index 100% rename from caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestCustomResource.java rename to caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestCustomResource.java diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestReconciler.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestReconciler.java similarity index 100% rename from caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestReconciler.java rename to caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestReconciler.java diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestSpec.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestSpec.java similarity index 100% rename from caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestSpec.java rename to caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestSpec.java diff --git a/caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestStatus.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestStatus.java similarity index 100% rename from caffein-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestStatus.java rename to caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestStatus.java diff --git a/caffein-bounded-cache-support/src/test/resources/log4j2.xml b/caffeine-bounded-cache-support/src/test/resources/log4j2.xml similarity index 100% rename from caffein-bounded-cache-support/src/test/resources/log4j2.xml rename to caffeine-bounded-cache-support/src/test/resources/log4j2.xml diff --git a/pom.xml b/pom.xml index b3a413ecf1..bcc55dc646 100644 --- a/pom.xml +++ b/pom.xml @@ -82,7 +82,7 @@ operator-framework micrometer-support sample-operators - caffein-bounded-cache-support + caffeine-bounded-cache-support From 760f57d18202dad08c718c4b0d97d4bf254b9fc5 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 21 Feb 2023 11:14:52 +0100 Subject: [PATCH 23/27] docs --- .../operator/api/config/ResourceConfiguration.java | 6 +++--- .../event/source/cache/KubernetesResourceFetcher.java | 5 +---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java index fe1caaa611..d3a8379d46 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java @@ -125,9 +125,9 @@ default Set getEffectiveNamespaces() { } /** - * See related - * method in fabric8 client. + * Replaces the item store in informer. See underling + * method in fabric8 client informer implementation. * * The main goal, is to be able to use limited caches. * diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java index 6c4a68f947..50b0f5ea8e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java @@ -32,10 +32,7 @@ public R fetchResource(String key) { .withName(resourceId.getName()).get()) .orElse(client.resources(rClass).withName(resourceId.getName()).get()); } - - /** - * This would not be needed - **/ + public static Function inverseNamespaceKeyFunction() { return s -> { int delimiterIndex = s.indexOf("/"); From 78c83f6bc8fecfc2078aa6d9c2bb7adc845e04a0 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 21 Feb 2023 12:38:54 +0100 Subject: [PATCH 24/27] fixes --- .../event/source/cache/KubernetesResourceFetcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java index 50b0f5ea8e..ad996afc36 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java @@ -32,7 +32,7 @@ public R fetchResource(String key) { .withName(resourceId.getName()).get()) .orElse(client.resources(rClass).withName(resourceId.getName()).get()); } - + public static Function inverseNamespaceKeyFunction() { return s -> { int delimiterIndex = s.indexOf("/"); From 50e351af173cad276ff12d71124a520ed3309129 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 21 Feb 2023 13:00:59 +0100 Subject: [PATCH 25/27] additional condition --- .../processing/event/source/cache/BoundedItemStore.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java index 4e29304173..0c897b5be6 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java @@ -121,14 +121,19 @@ protected R refreshMissingStateFromServer(String key) { var newRes = resourceFetcher.fetchResource(key); synchronized (this) { log.debug("Fetched resource: {}", newRes); + var actual = cache.get(key); if (newRes == null) { - existingMinimalResources.remove(key); + // double-checking if actual, not received since. + // If received we just return. Since the resource from informer should be always leading, + // even if the fetched resource is null, this will be eventually received as an event. + if (actual == null) { + existingMinimalResources.remove(key); + } return null; } // Just want to put the fetched resource if there is still no resource published from // different source. In case of informers actually multiple events might arrive, therefore non // fetched resource should take always precedence. - var actual = cache.get(key); if (actual == null) { cache.put(key, newRes); existingMinimalResources.put(key, createMinimalResource(newRes)); From 139b27d9c79fb212b5bdb6c10b8956aebb0c9774 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 21 Feb 2023 15:49:54 +0100 Subject: [PATCH 26/27] fix on return actual --- .../processing/event/source/cache/BoundedItemStore.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java index 0c897b5be6..65ec60a953 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java @@ -128,8 +128,9 @@ protected R refreshMissingStateFromServer(String key) { // even if the fetched resource is null, this will be eventually received as an event. if (actual == null) { existingMinimalResources.remove(key); + } else { + return actual; } - return null; } // Just want to put the fetched resource if there is still no resource published from // different source. In case of informers actually multiple events might arrive, therefore non From c3de33b65407b2afb92628a2c86bd020b8d462c2 Mon Sep 17 00:00:00 2001 From: csviri Date: Tue, 21 Feb 2023 16:03:36 +0100 Subject: [PATCH 27/27] impl fix --- .../operator/processing/event/source/cache/BoundedItemStore.java | 1 + 1 file changed, 1 insertion(+) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java index 65ec60a953..4f0fcad280 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java @@ -128,6 +128,7 @@ protected R refreshMissingStateFromServer(String key) { // even if the fetched resource is null, this will be eventually received as an event. if (actual == null) { existingMinimalResources.remove(key); + return null; } else { return actual; }