From 9932dd8c04c854ff648dcf42659a168f617b5701 Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 20 Apr 2022 14:29:21 +0200 Subject: [PATCH 01/18] feat: all event sources are multi resource owners --- .../api/reconciler/DefaultContext.java | 13 +-- .../dependent/RecentOperationCacheFiller.java | 2 +- .../PerResourcePollingDependentResource.java | 9 ++ .../external/PollingDependentResource.java | 20 ++++- .../ExternalResourceCachingEventSource.java | 84 +++++++++++++------ .../event/source/AbstractEventSource.java | 1 - .../source/AbstractResourceEventSource.java | 2 +- .../event/source/CachingEventSource.java | 8 +- .../processing/event/source/IDProvider.java | 7 ++ .../event/source/ResourceEventSource.java | 4 +- .../inbound/CachingInboundEventSource.java | 3 +- .../source/informer/InformerEventSource.java | 7 +- .../informer/ManagedInformerEventSource.java | 4 +- .../PerResourcePollingEventSource.java | 19 ++--- .../source/polling/PollingEventSource.java | 72 ++++++++-------- ...xternalResourceCachingEventSourceTest.java | 2 +- .../PerResourcePollingEventSourceTest.java | 14 ++-- .../dependent/SchemaDependentResource.java | 5 ++ .../SchemaPollingResourceFetcher.java | 5 +- 19 files changed, 168 insertions(+), 113 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDProvider.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java index 00a94390c6..8284058803 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.api.reconciler; -import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.Set; @@ -10,7 +9,6 @@ import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedDependentResourceContext; import io.javaoperatorsdk.operator.processing.Controller; -import io.javaoperatorsdk.operator.processing.MultiResourceOwner; public class DefaultContext

implements Context

{ @@ -37,16 +35,7 @@ public Optional getRetryInfo() { @SuppressWarnings("unchecked") public Set getSecondaryResources(Class expectedType) { return controller.getEventSourceManager().getEventSourcesFor(expectedType).stream() - .map( - es -> { - if (es instanceof MultiResourceOwner) { - return ((MultiResourceOwner) es).getSecondaryResources(primaryResource); - } else { - return es.getSecondaryResource(primaryResource) - .map(List::of) - .orElse(Collections.emptyList()); - } - }) + .map(es -> es.getSecondaryResources(primaryResource)) .flatMap(List::stream) .collect(Collectors.toSet()); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/RecentOperationCacheFiller.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/RecentOperationCacheFiller.java index 7e27537d49..cb84783a4f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/RecentOperationCacheFiller.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/RecentOperationCacheFiller.java @@ -6,5 +6,5 @@ public interface RecentOperationCacheFiller { void handleRecentResourceCreate(ResourceID resourceID, R resource); - void handleRecentResourceUpdate(ResourceID resourceID, R resource, R previousResourceVersion); + void handleRecentResourceUpdate(ResourceID resourceID, R resource, R previousVersionOfResource); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java index b598073a21..4cb0e03894 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java @@ -5,9 +5,13 @@ import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingEventSource; +import java.util.Optional; + public abstract class PerResourcePollingDependentResource extends AbstractPollingDependentResource implements PerResourcePollingEventSource.ResourceFetcher { + + public PerResourcePollingDependentResource(Class resourceType) { super(resourceType); } @@ -22,4 +26,9 @@ protected ExternalResourceCachingEventSource createEventSource( return new PerResourcePollingEventSource<>(this, context.getPrimaryCache(), getPollingPeriod(), resourceType()); } + + @Override + public Optional fetchResource(P primaryResource) { + return Optional.empty(); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java index 1cc6e22afe..c641f72874 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java @@ -1,28 +1,40 @@ package io.javaoperatorsdk.operator.processing.dependent.external; import java.util.Map; +import java.util.Optional; +import java.util.Set; import java.util.function.Supplier; import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.IDProvider; import io.javaoperatorsdk.operator.processing.event.source.polling.PollingEventSource; public abstract class PollingDependentResource - extends AbstractPollingDependentResource implements Supplier> { + extends AbstractPollingDependentResource implements Supplier>> { - public PollingDependentResource(Class resourceType) { + private final IDProvider idProvider; + + public PollingDependentResource(Class resourceType, IDProvider idProvider) { super(resourceType); + this.idProvider = idProvider; } - public PollingDependentResource(Class resourceType, long pollingPeriod) { + public PollingDependentResource(Class resourceType, long pollingPeriod, IDProvider idProvider) { super(resourceType, pollingPeriod); + this.idProvider = idProvider; } @Override protected ExternalResourceCachingEventSource createEventSource( EventSourceContext

context) { - return new PollingEventSource<>(this, getPollingPeriod(), resourceType()); + return new PollingEventSource<>(this, getPollingPeriod(), resourceType(), idProvider); + } + + @Override + public Optional fetchResource(P primaryResource) { + return (PollingEventSource)eventSource().; } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ExternalResourceCachingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ExternalResourceCachingEventSource.java index b4bb0f7ef7..9cd2a0770c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ExternalResourceCachingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ExternalResourceCachingEventSource.java @@ -1,60 +1,92 @@ package io.javaoperatorsdk.operator.processing.event; -import java.util.Optional; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationCacheFiller; -import io.javaoperatorsdk.operator.processing.event.source.CachingEventSource; +import io.javaoperatorsdk.operator.processing.event.source.*; -public class ExternalResourceCachingEventSource - extends CachingEventSource implements RecentOperationCacheFiller { +public abstract class ExternalResourceCachingEventSource + extends AbstractResourceEventSource implements RecentOperationCacheFiller { - public ExternalResourceCachingEventSource(Class resourceClass) { + protected final IDProvider idProvider; + protected Map> cache = new ConcurrentHashMap<>(); + + protected ExternalResourceCachingEventSource(Class resourceClass, IDProvider idProvider) { super(resourceClass); + this.idProvider = idProvider; } - public synchronized void handleDelete(ResourceID relatedResourceID) { + public synchronized void handleDelete(ResourceID primaryID, String resourceID) { if (!isRunning()) { return; } - var cachedValue = cache.get(relatedResourceID); - cache.remove(relatedResourceID); + var cachedValues = cache.get(primaryID); + R cachedResource = cachedValues.remove(resourceID); + + if (cache.isEmpty()) { + cache.remove(primaryID); + } // we only propagate event if the resource was previously in cache - if (cachedValue.isPresent()) { - getEventHandler().handleEvent(new Event(relatedResourceID)); + if (cachedResource != null) { + getEventHandler().handleEvent(new Event(primaryID)); } } - public synchronized void handleEvent(R value, ResourceID relatedResourceID) { + public synchronized void handleEvent(R resource, ResourceID primaryID) { if (!isRunning()) { return; } - var cachedValue = cache.get(relatedResourceID); - if (cachedValue.map(v -> !v.equals(value)).orElse(true)) { - cache.put(relatedResourceID, value); - getEventHandler().handleEvent(new Event(relatedResourceID)); + var resourceId = idProvider.getID(resource); + var cachedValues = cache.get(primaryID); + if (cachedValues == null) { + Map values = new HashMap<>(); + values.put(resourceId,resource); + cache.put(primaryID,values); + } else { + var actualResource = cachedValues.get(resourceId); + cachedValues.put(resourceId,resource); + if (actualResource != null) { + if (!actualResource.equals(resource)) { + getEventHandler().handleEvent(new Event(primaryID)); + } + } } } @Override - public synchronized void handleRecentResourceCreate(ResourceID resourceID, R resource) { - if (cache.get(resourceID).isEmpty()) { - cache.put(resourceID, resource); + public synchronized void handleRecentResourceCreate(ResourceID primaryID, R resource) { + var actualValues = cache.get(primaryID); + var resourceId = idProvider.getID(resource); + if (actualValues == null) { + actualValues = new HashMap<>(); + cache.put(primaryID,actualValues); + actualValues.put(resourceId,resource); + } else { + actualValues.computeIfAbsent(resourceId,r -> resource); } } @Override - public synchronized void handleRecentResourceUpdate(ResourceID resourceID, R resource, - R previousResourceVersion) { - cache.get(resourceID).ifPresent(r -> { - if (r.equals(previousResourceVersion)) { - cache.put(resourceID, resource); + public synchronized void handleRecentResourceUpdate(ResourceID primaryID, R resource, + R previousVersionOfResource) { + var actualValues= cache.get(primaryID); + if (actualValues != null) { + var resourceId = idProvider.getID(resource); + R actualResource = actualValues.get(resourceId); + if (actualResource.equals(previousVersionOfResource)) { + actualValues.put(resourceId,resource); } - }); + } } @Override - public Optional getSecondaryResource(P primary) { - return cache.get(ResourceID.fromResource(primary)); + public List getSecondaryResources(P primary) { + return new ArrayList<>(cache.get(ResourceID.fromResource(primary)).values()); + } + + protected UpdatableCache initCache() { + return new ConcurrentHashMapCache<>(); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractEventSource.java index 6767e974e2..a6666ba81d 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractEventSource.java @@ -4,7 +4,6 @@ import io.javaoperatorsdk.operator.processing.event.EventHandler; public abstract class AbstractEventSource implements EventSource { - private EventHandler handler; private volatile boolean running = false; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractResourceEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractResourceEventSource.java index b22899f246..2d568f7a5b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractResourceEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractResourceEventSource.java @@ -2,7 +2,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; -public abstract class AbstractResourceEventSource

+public abstract class AbstractResourceEventSource extends AbstractEventSource implements ResourceEventSource { private final Class resourceClass; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CachingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CachingEventSource.java index 8453651b6b..141abb8816 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CachingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CachingEventSource.java @@ -14,7 +14,7 @@ * @param represents the type of resources (usually external non-kubernetes ones) being handled. */ public abstract class CachingEventSource - extends AbstractResourceEventSource implements Cache { + extends AbstractResourceEventSource implements Cache { protected UpdatableCache cache; @@ -43,12 +43,8 @@ public Stream list(Predicate predicate) { return cache.list(predicate); } - protected UpdatableCache initCache() { - return new ConcurrentHashMapCache<>(); - } - public Optional getCachedValue(ResourceID resourceID) { return cache.get(resourceID); } - + protected abstract UpdatableCache initCache(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDProvider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDProvider.java new file mode 100644 index 0000000000..8901e73d33 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDProvider.java @@ -0,0 +1,7 @@ +package io.javaoperatorsdk.operator.processing.event.source; + +public interface IDProvider { + + String getID(R resource); + +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java index d65208f746..e0f031c044 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java @@ -1,9 +1,9 @@ package io.javaoperatorsdk.operator.processing.event.source; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.ResourceOwner; +import io.javaoperatorsdk.operator.processing.MultiResourceOwner; public interface ResourceEventSource extends EventSource, - ResourceOwner { + MultiResourceOwner { } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java index 34f37a048b..60a22b4741 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java @@ -8,7 +8,7 @@ public class CachingInboundEventSource extends ExternalResourceCachingEventSource { public CachingInboundEventSource(Class resourceClass) { - super(resourceClass); + super(resourceClass, idProvider); } public void handleResourceEvent(T resource, ResourceID relatedResourceID) { @@ -18,4 +18,5 @@ public void handleResourceEvent(T resource, ResourceID relatedResourceID) { public void handleResourceDeleteEvent(ResourceID resourceID) { super.handleDelete(resourceID); } + } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java index 08facecbb7..9a23bf3afa 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java @@ -13,7 +13,6 @@ import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationEventFilter; -import io.javaoperatorsdk.operator.processing.MultiResourceOwner; import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.EventHandler; import io.javaoperatorsdk.operator.processing.event.ResourceID; @@ -66,7 +65,7 @@ */ public class InformerEventSource extends ManagedInformerEventSource> - implements MultiResourceOwner, ResourceEventHandler, RecentOperationEventFilter { + implements ResourceEventHandler, RecentOperationEventFilter { private static final Logger log = LoggerFactory.getLogger(InformerEventSource.class); @@ -190,9 +189,9 @@ public InformerConfiguration getConfiguration() { @Override public synchronized void handleRecentResourceUpdate(ResourceID resourceID, R resource, - R previousResourceVersion) { + R previousVersionOfResource) { handleRecentCreateOrUpdate(resource, - () -> super.handleRecentResourceUpdate(resourceID, resource, previousResourceVersion)); + () -> super.handleRecentResourceUpdate(resourceID, resource, previousVersionOfResource)); } @Override diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java index 606b0bc962..95aba48ad7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java @@ -74,9 +74,9 @@ public void stop() { @Override public void handleRecentResourceUpdate(ResourceID resourceID, R resource, - R previousResourceVersion) { + R previousVersionOfResource) { temporaryResourceCache.putUpdatedResource(resource, - previousResourceVersion.getMetadata().getResourceVersion()); + previousVersionOfResource.getMetadata().getResourceVersion()); } @Override diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java index 8b04dc9e57..809d120397 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java @@ -1,12 +1,10 @@ package io.javaoperatorsdk.operator.processing.event.source.polling; -import java.util.Map; -import java.util.Optional; -import java.util.Timer; -import java.util.TimerTask; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; +import io.javaoperatorsdk.operator.processing.event.source.IDProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,9 +46,10 @@ public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, } public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, - Cache

resourceCache, long period, - Predicate

registerPredicate, Class resourceClass) { - super(resourceClass); + Cache

resourceCache, long period, + Predicate

registerPredicate, Class resourceClass, + IDProvider idProvider) { + super(resourceClass, idProvider); this.resourceFetcher = resourceFetcher; this.resourceCache = resourceCache; this.period = period; @@ -58,7 +57,7 @@ public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, } private void pollForResource(P resource) { - var value = resourceFetcher.fetchResource(resource); + var value = resourceFetcher.fetchResources(resource); var resourceID = ResourceID.fromResource(resource); if (value.isEmpty()) { super.handleDelete(resourceID); @@ -70,7 +69,7 @@ private void pollForResource(P resource) { private Optional getAndCacheResource(ResourceID resourceID) { var resource = resourceCache.get(resourceID); if (resource.isPresent()) { - var value = resourceFetcher.fetchResource(resource.get()); + var value = resourceFetcher.fetchResources(resource.get()); value.ifPresent(v -> cache.put(resourceID, v)); return value; } @@ -153,7 +152,7 @@ public Optional getValueFromCacheOrSupplier(ResourceID resourceID) { } public interface ResourceFetcher { - Optional fetchResource(P primaryResource); + Set fetchResources(P primaryResource); } @Override diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java index dcdb2cdda9..7ceb50c999 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java @@ -1,11 +1,10 @@ package io.javaoperatorsdk.operator.processing.event.source.polling; -import java.util.Map; -import java.util.Optional; -import java.util.Timer; -import java.util.TimerTask; +import java.util.*; import java.util.function.Supplier; +import java.util.stream.Collectors; +import io.javaoperatorsdk.operator.processing.event.source.IDProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,19 +21,19 @@ * not contain the target resource it means it is not created yet or was deleted while an operator * was not running. * - *

- * Another caveat with this is if the cached object is checked in the reconciler and created since - * not in the cache it should be manually added to the cache, since it can happen that the + *

Another caveat with this is if the cached object is checked in the reconciler and created + * since not in the cache it should be manually added to the cache, since it can happen that the * reconciler is triggered before the cache is propagated with the new resource from a scheduled * execution. See {@link #put(ResourceID, Object)} method. So the generic workflow in reconciler * should be: * *

* * @param type of the polled resource @@ -46,12 +45,15 @@ public class PollingEventSource private static final Logger log = LoggerFactory.getLogger(PollingEventSource.class); private final Timer timer = new Timer(); - private final Supplier> supplierToPoll; + private final Supplier>> supplierToPoll; private final long period; - public PollingEventSource(Supplier> supplier, - long period, Class resourceClass) { - super(resourceClass); + public PollingEventSource( + Supplier>> supplier, + long period, + Class resourceClass, + IDProvider idProvider) { + super(resourceClass, idProvider); this.supplierToPoll = supplier; this.period = period; } @@ -75,14 +77,29 @@ public void run() { period); } - protected void getStateAndFillCache() { + // todo can be optimized; + protected synchronized void getStateAndFillCache() { var values = supplierToPoll.get(); - values.forEach((k, v) -> super.handleEvent(v, k)); - cache.keys().filter(e -> !values.containsKey(e)).forEach(super::handleDelete); - } + HashMap toDelete = new HashMap<>(); + + cache.forEach( + (primaryID, resourcesMap) -> { + var newIds = + values.get(primaryID).stream() + .map(idProvider::getID) + .collect(Collectors.toSet()); + resourcesMap.forEach( + (actualID, actualResource) -> { + if (!newIds.contains(actualID)) { + toDelete.put(primaryID,actualID); + } + }); + }); + toDelete.forEach(super::handleDelete); + - public void put(ResourceID key, R resource) { - cache.put(key, resource); + values.forEach( + (k, v) -> v.forEach(r -> handleEvent(r, k))); } @Override @@ -90,15 +107,4 @@ public void stop() throws OperatorException { super.stop(); timer.cancel(); } - - /** - * See {@link PerResourcePollingEventSource} for more info. - * - * @param primary custom resource - * @return related resource - */ - @Override - public Optional getSecondaryResource(P primary) { - return getCachedValue(ResourceID.fromResource(primary)); - } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java index 9ed4e25342..b8ba8d9b6a 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java @@ -69,7 +69,7 @@ public void noEventOnDeleteIfResourceWasNotInCacheBefore() { public static class SimpleExternalCachingEventSource extends ExternalResourceCachingEventSource { public SimpleExternalCachingEventSource() { - super(SampleExternalResource.class); + super(SampleExternalResource.class, idProvider); } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java index eb8d80c0c1..04a00ac960 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java @@ -35,7 +35,7 @@ class PerResourcePollingEventSourceTest extends @BeforeEach public void setup() { when(resourceCache.get(any())).thenReturn(Optional.of(testCustomResource)); - when(supplier.fetchResource(any())) + when(supplier.fetchResources(any())) .thenReturn(Optional.of(SampleExternalResource.testResource1())); setUpSource(new PerResourcePollingEventSource<>(supplier, resourceCache, PERIOD, @@ -47,7 +47,7 @@ public void pollsTheResourceAfterAwareOfIt() throws InterruptedException { source.onResourceCreated(testCustomResource); Thread.sleep(3 * PERIOD); - verify(supplier, atLeast(2)).fetchResource(eq(testCustomResource)); + verify(supplier, atLeast(2)).fetchResources(eq(testCustomResource)); verify(eventHandler, times(1)).handleEvent(any()); } @@ -59,31 +59,31 @@ public void registeringTaskOnAPredicate() throws InterruptedException { source.onResourceCreated(testCustomResource); Thread.sleep(2 * PERIOD); - verify(supplier, times(0)).fetchResource(eq(testCustomResource)); + verify(supplier, times(0)).fetchResources(eq(testCustomResource)); testCustomResource.getMetadata().setGeneration(2L); source.onResourceUpdated(testCustomResource, testCustomResource); Thread.sleep(2 * PERIOD); - verify(supplier, atLeast(1)).fetchResource(eq(testCustomResource)); + verify(supplier, atLeast(1)).fetchResources(eq(testCustomResource)); } @Test public void propagateEventOnDeletedResource() throws InterruptedException { source.onResourceCreated(testCustomResource); - when(supplier.fetchResource(any())) + when(supplier.fetchResources(any())) .thenReturn(Optional.of(SampleExternalResource.testResource1())) .thenReturn(Optional.empty()); Thread.sleep(3 * PERIOD); - verify(supplier, atLeast(2)).fetchResource(eq(testCustomResource)); + verify(supplier, atLeast(2)).fetchResources(eq(testCustomResource)); verify(eventHandler, times(2)).handleEvent(any()); } @Test public void getsValueFromCacheOrSupplier() throws InterruptedException { source.onResourceCreated(testCustomResource); - when(supplier.fetchResource(any())) + when(supplier.fetchResources(any())) .thenReturn(Optional.empty()) .thenReturn(Optional.of(SampleExternalResource.testResource1())); diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java index 3f370bc7a2..f91359e23c 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java @@ -5,6 +5,7 @@ import java.sql.SQLException; import java.util.Base64; import java.util.Optional; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -99,4 +100,8 @@ public static String decode(String value) { return new String(Base64.getDecoder().decode(value.getBytes())); } + @Override + public Set fetchResources(MySQLSchema primaryResource) { + return null; + } } diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaPollingResourceFetcher.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaPollingResourceFetcher.java index 2830a4b08b..6c1f3bab85 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaPollingResourceFetcher.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaPollingResourceFetcher.java @@ -1,6 +1,7 @@ package io.javaoperatorsdk.operator.sample.dependent; import java.util.Optional; +import java.util.Set; import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingEventSource; import io.javaoperatorsdk.operator.sample.MySQLDbConfig; @@ -18,7 +19,7 @@ public SchemaPollingResourceFetcher(MySQLDbConfig mySQLDbConfig) { } @Override - public Optional fetchResource(MySQLSchema resource) { - return schemaService.getSchema(resource.getMetadata().getName()); + public Set fetchResources(MySQLSchema primaryResource) { + return null; } } From b812ba1aa1fe7b8f69cb31cb5236c156a4c71aae Mon Sep 17 00:00:00 2001 From: csviri Date: Wed, 20 Apr 2022 17:03:49 +0200 Subject: [PATCH 02/18] wip --- .../processing/MultiResourceOwner.java | 11 +-- .../AbstractCachingDependentResource.java | 2 - .../AbstractPollingDependentResource.java | 5 +- .../PerResourcePollingDependentResource.java | 6 +- .../external/PollingDependentResource.java | 13 +--- .../ExternalResourceCachingEventSource.java | 18 +++-- .../processing/event/source/IDMapper.java | 6 ++ .../processing/event/source/IDProvider.java | 7 -- .../inbound/CachingInboundEventSource.java | 15 ++-- .../source/informer/InformerEventSource.java | 5 +- .../PerResourcePollingEventSource.java | 78 ++++++++++--------- .../source/polling/PollingEventSource.java | 5 +- .../PerResourcePollingEventSourceTest.java | 7 +- .../polling/PollingEventSourceTest.java | 22 +++--- .../dependent/SchemaDependentResource.java | 23 +++--- 15 files changed, 114 insertions(+), 109 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java delete mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDProvider.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MultiResourceOwner.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MultiResourceOwner.java index d3cc6b3770..3cc3926a65 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MultiResourceOwner.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MultiResourceOwner.java @@ -2,22 +2,23 @@ import java.util.List; import java.util.Optional; +import java.util.Set; import io.fabric8.kubernetes.api.model.HasMetadata; public interface MultiResourceOwner extends ResourceOwner { default Optional getSecondaryResource(P primary) { - var list = getSecondaryResources(primary); - if (list.isEmpty()) { + var resources = getSecondaryResources(primary); + if (resources.isEmpty()) { return Optional.empty(); - } else if (list.size() == 1) { - return Optional.of(list.get(0)); + } else if (resources.size() == 1) { + return Optional.of(resources.iterator().next()); } else { throw new IllegalStateException("More than 1 secondary resource related to primary"); } } - List getSecondaryResources(P primary); + Set getSecondaryResources(P primary); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java index 036a507de9..1b2d388088 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java @@ -15,8 +15,6 @@ protected AbstractCachingDependentResource(Class resourceType) { this.resourceType = resourceType; } - public abstract Optional fetchResource(P primaryResource); - @Override public Class resourceType() { return resourceType; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java index 8c91dea15d..41060a0668 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java @@ -1,9 +1,12 @@ package io.javaoperatorsdk.operator.processing.dependent.external; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.event.source.IDMapper; + +import java.util.function.Function; public abstract class AbstractPollingDependentResource - extends AbstractCachingDependentResource { + extends AbstractCachingDependentResource implements IDMapper { public static final int DEFAULT_POLLING_PERIOD = 5000; private long pollingPeriod; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java index 4cb0e03894..317c772fbb 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java @@ -24,11 +24,7 @@ public PerResourcePollingDependentResource(Class resourceType, long pollingPe protected ExternalResourceCachingEventSource createEventSource( EventSourceContext

context) { return new PerResourcePollingEventSource<>(this, context.getPrimaryCache(), - getPollingPeriod(), resourceType()); + getPollingPeriod(), resourceType(), this); } - @Override - public Optional fetchResource(P primaryResource) { - return Optional.empty(); - } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java index c641f72874..f779c24c54 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java @@ -1,7 +1,6 @@ package io.javaoperatorsdk.operator.processing.dependent.external; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.function.Supplier; @@ -9,20 +8,20 @@ import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.ResourceID; -import io.javaoperatorsdk.operator.processing.event.source.IDProvider; +import io.javaoperatorsdk.operator.processing.event.source.IDMapper; import io.javaoperatorsdk.operator.processing.event.source.polling.PollingEventSource; public abstract class PollingDependentResource extends AbstractPollingDependentResource implements Supplier>> { - private final IDProvider idProvider; + private final IDMapper idProvider; - public PollingDependentResource(Class resourceType, IDProvider idProvider) { + public PollingDependentResource(Class resourceType, IDMapper idProvider) { super(resourceType); this.idProvider = idProvider; } - public PollingDependentResource(Class resourceType, long pollingPeriod, IDProvider idProvider) { + public PollingDependentResource(Class resourceType, long pollingPeriod, IDMapper idProvider) { super(resourceType, pollingPeriod); this.idProvider = idProvider; } @@ -33,8 +32,4 @@ protected ExternalResourceCachingEventSource createEventSource( return new PollingEventSource<>(this, getPollingPeriod(), resourceType(), idProvider); } - @Override - public Optional fetchResource(P primaryResource) { - return (PollingEventSource)eventSource().; - } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ExternalResourceCachingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ExternalResourceCachingEventSource.java index 9cd2a0770c..072676cbc7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ExternalResourceCachingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ExternalResourceCachingEventSource.java @@ -2,6 +2,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationCacheFiller; @@ -10,10 +11,11 @@ public abstract class ExternalResourceCachingEventSource extends AbstractResourceEventSource implements RecentOperationCacheFiller { - protected final IDProvider idProvider; + protected final IDMapper idProvider; + protected Map> cache = new ConcurrentHashMap<>(); - protected ExternalResourceCachingEventSource(Class resourceClass, IDProvider idProvider) { + protected ExternalResourceCachingEventSource(Class resourceClass, IDMapper idProvider) { super(resourceClass); this.idProvider = idProvider; } @@ -25,7 +27,7 @@ public synchronized void handleDelete(ResourceID primaryID, String resourceID) { var cachedValues = cache.get(primaryID); R cachedResource = cachedValues.remove(resourceID); - if (cache.isEmpty()) { + if (cachedValues.isEmpty()) { cache.remove(primaryID); } // we only propagate event if the resource was previously in cache @@ -38,7 +40,7 @@ public synchronized void handleEvent(R resource, ResourceID primaryID) { if (!isRunning()) { return; } - var resourceId = idProvider.getID(resource); + var resourceId = idProvider.apply(resource); var cachedValues = cache.get(primaryID); if (cachedValues == null) { Map values = new HashMap<>(); @@ -58,7 +60,7 @@ public synchronized void handleEvent(R resource, ResourceID primaryID) { @Override public synchronized void handleRecentResourceCreate(ResourceID primaryID, R resource) { var actualValues = cache.get(primaryID); - var resourceId = idProvider.getID(resource); + var resourceId = idProvider.apply(resource); if (actualValues == null) { actualValues = new HashMap<>(); cache.put(primaryID,actualValues); @@ -73,7 +75,7 @@ public synchronized void handleRecentResourceUpdate(ResourceID primaryID, R reso R previousVersionOfResource) { var actualValues= cache.get(primaryID); if (actualValues != null) { - var resourceId = idProvider.getID(resource); + var resourceId = idProvider.apply(resource); R actualResource = actualValues.get(resourceId); if (actualResource.equals(previousVersionOfResource)) { actualValues.put(resourceId,resource); @@ -82,8 +84,8 @@ public synchronized void handleRecentResourceUpdate(ResourceID primaryID, R reso } @Override - public List getSecondaryResources(P primary) { - return new ArrayList<>(cache.get(ResourceID.fromResource(primary)).values()); + public Set getSecondaryResources(P primary) { + return new HashSet<>(cache.get(ResourceID.fromResource(primary)).values()); } protected UpdatableCache initCache() { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java new file mode 100644 index 0000000000..62f7928763 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java @@ -0,0 +1,6 @@ +package io.javaoperatorsdk.operator.processing.event.source; + +import java.util.function.Function; + +public interface IDMapper extends Function { +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDProvider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDProvider.java deleted file mode 100644 index 8901e73d33..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDProvider.java +++ /dev/null @@ -1,7 +0,0 @@ -package io.javaoperatorsdk.operator.processing.event.source; - -public interface IDProvider { - - String getID(R resource); - -} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java index 60a22b4741..723dc4aeba 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java @@ -3,20 +3,21 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.IDMapper; -public class CachingInboundEventSource - extends ExternalResourceCachingEventSource { +public class CachingInboundEventSource + extends ExternalResourceCachingEventSource { - public CachingInboundEventSource(Class resourceClass) { + public CachingInboundEventSource(Class resourceClass, IDMapper idProvider) { super(resourceClass, idProvider); } - public void handleResourceEvent(T resource, ResourceID relatedResourceID) { - super.handleEvent(resource, relatedResourceID); + public void handleResourceEvent(R resource, ResourceID primaryID) { + super.handleEvent(resource, primaryID); } - public void handleResourceDeleteEvent(ResourceID resourceID) { - super.handleDelete(resourceID); + public void handleResourceDeleteEvent(ResourceID primaryID,String resourceID) { + super.handleDelete(primaryID,resourceID); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java index 9a23bf3afa..f4a6529aa2 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import org.slf4j.Logger; @@ -176,11 +177,11 @@ private void propagateEvent(R object) { } @Override - public List getSecondaryResources(P primary) { + public Set getSecondaryResources(P primary) { var secondaryIDs = primaryToSecondaryIndex.getSecondaryResources(ResourceID.fromResource(primary)); return secondaryIDs.stream().map(this::get).flatMap(Optional::stream) - .collect(Collectors.toList()); + .collect(Collectors.toSet()); } public InformerConfiguration getConfiguration() { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java index 809d120397..60fa62f71a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java @@ -2,9 +2,11 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import java.util.function.Predicate; +import java.util.stream.Collectors; -import io.javaoperatorsdk.operator.processing.event.source.IDProvider; +import io.javaoperatorsdk.operator.processing.event.source.IDMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,14 +43,14 @@ public class PerResourcePollingEventSource private final long period; public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, - Cache

resourceCache, long period, Class resourceClass) { - this(resourceFetcher, resourceCache, period, null, resourceClass); + Cache

resourceCache, long period, Class resourceClass, IDMapper idProvider) { + this(resourceFetcher, resourceCache, period, null, resourceClass, idProvider); } public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, Cache

resourceCache, long period, Predicate

registerPredicate, Class resourceClass, - IDProvider idProvider) { + IDMapper idProvider) { super(resourceClass, idProvider); this.resourceFetcher = resourceFetcher; this.resourceCache = resourceCache; @@ -56,24 +58,28 @@ public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, this.registerPredicate = registerPredicate; } - private void pollForResource(P resource) { - var value = resourceFetcher.fetchResources(resource); - var resourceID = ResourceID.fromResource(resource); - if (value.isEmpty()) { - super.handleDelete(resourceID); - } else { - super.handleEvent(value.get(), resourceID); - } + private void pollForResource(P primary) { + var primaryID = ResourceID.fromResource(primary); + var value = resourceFetcher.fetchResources(primary); + var newResourceIDs = value.stream().map(idProvider::apply).collect(Collectors.toSet()); + var cachedValues = cache.get(primaryID); + + Set toDelete = cachedValues.keySet().stream().filter(r -> !newResourceIDs.contains(r)) + .collect(Collectors.toSet()); + + toDelete.forEach(resourceID -> handleDelete(primaryID,resourceID)); + value.forEach(v->super.handleEvent(v,primaryID)); } - private Optional getAndCacheResource(ResourceID resourceID) { + private Set getAndCacheResource(ResourceID resourceID) { var resource = resourceCache.get(resourceID); if (resource.isPresent()) { - var value = resourceFetcher.fetchResources(resource.get()); - value.ifPresent(v -> cache.put(resourceID, v)); - return value; + var values = resourceFetcher.fetchResources(resource.get()); + values.forEach(r-> handleEvent(r,resourceID)); + return values; + } else { + return Collections.emptySet(); } - return Optional.empty(); } @Override @@ -105,19 +111,21 @@ private void checkAndRegisterTask(P resource) { var resourceID = ResourceID.fromResource(resource); if (timerTasks.get(resourceID) == null && (registerPredicate == null || registerPredicate.test(resource))) { - var task = new TimerTask() { - @Override - public void run() { - if (!isRunning()) { - log.debug("Event source not yet started. Will not run for: {}", resourceID); - return; - } - // always use up-to-date resource from cache - var res = resourceCache.get(resourceID); - res.ifPresentOrElse(r -> pollForResource(r), - () -> log.warn("No resource in cache for resource ID: {}", resourceID)); - } - }; + var task = + new TimerTask() { + @Override + public void run() { + if (!isRunning()) { + log.debug("Event source not yet started. Will not run for: {}", resourceID); + return; + } + // always use up-to-date resource from cache + var res = resourceCache.get(resourceID); + res.ifPresentOrElse( + PerResourcePollingEventSource.this::pollForResource, + () -> log.warn("No resource in cache for resource ID: {}", resourceID)); + } + }; timerTasks.put(resourceID, task); timer.schedule(task, 0, period); } @@ -131,7 +139,7 @@ public void run() { * @return the related resource for this event source */ @Override - public Optional getSecondaryResource(P primary) { + public Set getSecondaryResources(P primary) { return getValueFromCacheOrSupplier(ResourceID.fromResource(primary)); } @@ -142,10 +150,10 @@ public Optional getSecondaryResource(P primary) { * supplier. The value provided from the supplier is cached, but no new event is * propagated. */ - public Optional getValueFromCacheOrSupplier(ResourceID resourceID) { - var cachedValue = getCachedValue(resourceID); - if (cachedValue.isPresent()) { - return cachedValue; + public Set getValueFromCacheOrSupplier(ResourceID resourceID) { + var cachedValue = cache.get(resourceID); + if (cachedValue != null) { + return new HashSet<>(cachedValue.values()); } else { return getAndCacheResource(resourceID); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java index 7ceb50c999..b2d684f311 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java @@ -1,6 +1,7 @@ package io.javaoperatorsdk.operator.processing.event.source.polling; import java.util.*; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -52,7 +53,7 @@ public PollingEventSource( Supplier>> supplier, long period, Class resourceClass, - IDProvider idProvider) { + Function idProvider) { super(resourceClass, idProvider); this.supplierToPoll = supplier; this.period = period; @@ -86,7 +87,7 @@ protected synchronized void getStateAndFillCache() { (primaryID, resourcesMap) -> { var newIds = values.get(primaryID).stream() - .map(idProvider::getID) + .map(idProvider::apply) .collect(Collectors.toSet()); resourcesMap.forEach( (actualID, actualResource) -> { diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java index 04a00ac960..71475ee3c2 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java @@ -1,6 +1,7 @@ package io.javaoperatorsdk.operator.processing.event.source.polling; import java.util.Optional; +import java.util.Set; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -36,10 +37,10 @@ class PerResourcePollingEventSourceTest extends public void setup() { when(resourceCache.get(any())).thenReturn(Optional.of(testCustomResource)); when(supplier.fetchResources(any())) - .thenReturn(Optional.of(SampleExternalResource.testResource1())); + .thenReturn(Set.of(SampleExternalResource.testResource1())); setUpSource(new PerResourcePollingEventSource<>(supplier, resourceCache, PERIOD, - SampleExternalResource.class)); + SampleExternalResource.class,)); } @Test @@ -55,7 +56,7 @@ public void pollsTheResourceAfterAwareOfIt() throws InterruptedException { public void registeringTaskOnAPredicate() throws InterruptedException { setUpSource(new PerResourcePollingEventSource<>(supplier, resourceCache, PERIOD, testCustomResource -> testCustomResource.getMetadata().getGeneration() > 1, - SampleExternalResource.class)); + SampleExternalResource.class,)); source.onResourceCreated(testCustomResource); Thread.sleep(2 * PERIOD); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java index d866791147..d855c42342 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java @@ -2,6 +2,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.function.Supplier; import org.junit.jupiter.api.BeforeEach; @@ -20,9 +21,10 @@ class PollingEventSourceTest extends AbstractEventSourceTestBase, EventHandler> { - private Supplier> supplier = mock(Supplier.class); - private PollingEventSource pollingEventSource = - new PollingEventSource<>(supplier, 50, SampleExternalResource.class); + private Supplier>> supplier = mock(Supplier.class); + private final PollingEventSource pollingEventSource = + new PollingEventSource<>(supplier, 50L, SampleExternalResource.class, + (SampleExternalResource er) -> er.getName()+"#"+er.getValue()); @BeforeEach public void setup() { @@ -57,16 +59,16 @@ public void doesNotPropagateEventIfResourceNotChanged() throws InterruptedExcept verify(eventHandler, times(2)).handleEvent(any()); } - private Map testResponseWithOneValue() { - Map res = new HashMap<>(); - res.put(testResource1ID(), testResource1()); + private Map> testResponseWithOneValue() { + Map> res = new HashMap<>(); + res.put(testResource1ID(), Set.of(testResource1())); return res; } - private Map testResponseWithTwoValues() { - Map res = new HashMap<>(); - res.put(testResource1ID(), testResource1()); - res.put(testResource2ID(), testResource2()); + private Map> testResponseWithTwoValues() { + Map> res = new HashMap<>(); + res.put(testResource1ID(), Set.of(testResource1())); + res.put(testResource2ID(), Set.of(testResource2())); return res; } diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java index f91359e23c..013c5c2d8d 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java @@ -3,9 +3,7 @@ import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; -import java.util.Base64; -import java.util.Optional; -import java.util.Set; +import java.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -85,23 +83,22 @@ public void delete(MySQLSchema primary, Context context) { } } + public static String decode(String value) { + return new String(Base64.getDecoder().decode(value.getBytes())); + } + @Override - public Optional fetchResource(MySQLSchema primaryResource) { + public Set fetchResources(MySQLSchema primaryResource) { try (Connection connection = getConnection()) { - var schema = - SchemaService.getSchema(connection, primaryResource.getMetadata().getName()).orElse(null); - return Optional.ofNullable(schema); + return SchemaService.getSchema(connection, primaryResource.getMetadata().getName()) + .map(Set::of).orElse(Collections.emptySet()); } catch (SQLException e) { throw new RuntimeException("Error while trying read Schema", e); } } - public static String decode(String value) { - return new String(Base64.getDecoder().decode(value.getBytes())); - } - @Override - public Set fetchResources(MySQLSchema primaryResource) { - return null; + public String getID(Schema resource) { + return resource.getName(); } } From b210b4bc784f58531f958a2f7e8c91b81a6947c5 Mon Sep 17 00:00:00 2001 From: csviri Date: Thu, 21 Apr 2022 10:52:12 +0200 Subject: [PATCH 03/18] build passes --- .../api/reconciler/DefaultContext.java | 3 +- .../processing/MultiResourceOwner.java | 1 - .../AbstractPollingDependentResource.java | 8 +- .../PerResourcePollingDependentResource.java | 2 - .../external/PollingDependentResource.java | 3 +- .../ExternalResourceCachingEventSource.java | 95 ++++++++++++------- .../source/AbstractResourceEventSource.java | 2 +- .../event/source/CachingEventSource.java | 3 +- .../processing/event/source/IDMapper.java | 13 ++- .../ControllerResourceEventSource.java | 6 ++ .../inbound/CachingInboundEventSource.java | 14 ++- .../source/informer/InformerEventSource.java | 1 - .../PerResourcePollingEventSource.java | 60 +++++------- .../source/polling/PollingEventSource.java | 64 ++++++------- ...xternalResourceCachingEventSourceTest.java | 45 +++++---- .../event/source/SampleExternalResource.java | 2 +- .../PerResourcePollingEventSourceTest.java | 19 ++-- .../polling/PollingEventSourceTest.java | 6 +- .../dependent/SchemaDependentResource.java | 6 +- .../SchemaPollingResourceFetcher.java | 1 - 20 files changed, 188 insertions(+), 166 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java index 8284058803..0b6d90065b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.api.reconciler; -import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -36,7 +35,7 @@ public Optional getRetryInfo() { public Set getSecondaryResources(Class expectedType) { return controller.getEventSourceManager().getEventSourcesFor(expectedType).stream() .map(es -> es.getSecondaryResources(primaryResource)) - .flatMap(List::stream) + .flatMap(Set::stream) .collect(Collectors.toSet()); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MultiResourceOwner.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MultiResourceOwner.java index 3cc3926a65..12d19f84c1 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MultiResourceOwner.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MultiResourceOwner.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.processing; -import java.util.List; import java.util.Optional; import java.util.Set; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java index 41060a0668..fcde777b23 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java @@ -3,8 +3,6 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.event.source.IDMapper; -import java.util.function.Function; - public abstract class AbstractPollingDependentResource extends AbstractCachingDependentResource implements IDMapper { @@ -27,4 +25,10 @@ public void setPollingPeriod(long pollingPeriod) { public long getPollingPeriod() { return pollingPeriod; } + + // for now dependent resources support event sources only with one owned resource. + @Override + public String apply(R r) { + return IDMapper.singleResourceIDMapper().apply(r); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java index 317c772fbb..ee9e1b54be 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java @@ -5,8 +5,6 @@ import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingEventSource; -import java.util.Optional; - public abstract class PerResourcePollingDependentResource extends AbstractPollingDependentResource implements PerResourcePollingEventSource.ResourceFetcher { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java index f779c24c54..61460db1bf 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java @@ -21,7 +21,8 @@ public PollingDependentResource(Class resourceType, IDMapper idProvider) { this.idProvider = idProvider; } - public PollingDependentResource(Class resourceType, long pollingPeriod, IDMapper idProvider) { + public PollingDependentResource(Class resourceType, long pollingPeriod, + IDMapper idProvider) { super(resourceType, pollingPeriod); this.idProvider = idProvider; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ExternalResourceCachingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ExternalResourceCachingEventSource.java index 072676cbc7..8a872846f6 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ExternalResourceCachingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ExternalResourceCachingEventSource.java @@ -2,7 +2,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; +import java.util.stream.Collectors; import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationCacheFiller; @@ -11,84 +11,107 @@ public abstract class ExternalResourceCachingEventSource extends AbstractResourceEventSource implements RecentOperationCacheFiller { - protected final IDMapper idProvider; - - protected Map> cache = new ConcurrentHashMap<>(); + protected final IDMapper idMapper; - protected ExternalResourceCachingEventSource(Class resourceClass, IDMapper idProvider) { + protected Map> cache = new ConcurrentHashMap<>(); + + protected ExternalResourceCachingEventSource(Class resourceClass, IDMapper idMapper) { super(resourceClass); - this.idProvider = idProvider; + this.idMapper = idMapper; + } + + public synchronized void handleDelete(ResourceID primaryID) { + cache.remove(primaryID); } public synchronized void handleDelete(ResourceID primaryID, String resourceID) { + handleDelete(primaryID, Set.of(resourceID)); + } + + public synchronized void handleDelete(ResourceID primaryID, R resource) { + handleDelete(primaryID, Set.of(idMapper.apply(resource))); + } + + public synchronized void handleDelete(ResourceID primaryID, Set resourceID) { if (!isRunning()) { return; } var cachedValues = cache.get(primaryID); - R cachedResource = cachedValues.remove(resourceID); + var sizeBeforeRemove = cachedValues.size(); + resourceID.forEach(cachedValues::remove); if (cachedValues.isEmpty()) { cache.remove(primaryID); } - // we only propagate event if the resource was previously in cache - if (cachedResource != null) { + if (sizeBeforeRemove > cachedValues.size()) { getEventHandler().handleEvent(new Event(primaryID)); } } - public synchronized void handleEvent(R resource, ResourceID primaryID) { + public synchronized void handleResourcesUpdate(ResourceID primaryID, R actualResource) { + handleResourcesUpdate(primaryID, Set.of(actualResource)); + } + + public synchronized void handleResourcesUpdate(ResourceID primaryID, Set newResources) { if (!isRunning()) { return; } - var resourceId = idProvider.apply(resource); - var cachedValues = cache.get(primaryID); - if (cachedValues == null) { - Map values = new HashMap<>(); - values.put(resourceId,resource); - cache.put(primaryID,values); - } else { - var actualResource = cachedValues.get(resourceId); - cachedValues.put(resourceId,resource); - if (actualResource != null) { - if (!actualResource.equals(resource)) { - getEventHandler().handleEvent(new Event(primaryID)); - } - } + var cachedResources = cache.get(primaryID); + var newResourcesMap = newResources.stream().collect(Collectors.toMap(idMapper, r -> r)); + cache.put(primaryID, newResourcesMap); + if (!newResourcesMap.equals(cachedResources)) { + getEventHandler().handleEvent(new Event(primaryID)); } } @Override public synchronized void handleRecentResourceCreate(ResourceID primaryID, R resource) { var actualValues = cache.get(primaryID); - var resourceId = idProvider.apply(resource); + var resourceId = idMapper.apply(resource); if (actualValues == null) { actualValues = new HashMap<>(); - cache.put(primaryID,actualValues); - actualValues.put(resourceId,resource); + cache.put(primaryID, actualValues); + actualValues.put(resourceId, resource); } else { - actualValues.computeIfAbsent(resourceId,r -> resource); + actualValues.computeIfAbsent(resourceId, r -> resource); } } @Override - public synchronized void handleRecentResourceUpdate(ResourceID primaryID, R resource, - R previousVersionOfResource) { - var actualValues= cache.get(primaryID); + public synchronized void handleRecentResourceUpdate( + ResourceID primaryID, R resource, R previousVersionOfResource) { + var actualValues = cache.get(primaryID); if (actualValues != null) { - var resourceId = idProvider.apply(resource); + var resourceId = idMapper.apply(resource); R actualResource = actualValues.get(resourceId); if (actualResource.equals(previousVersionOfResource)) { - actualValues.put(resourceId,resource); + actualValues.put(resourceId, resource); } } } @Override public Set getSecondaryResources(P primary) { - return new HashSet<>(cache.get(ResourceID.fromResource(primary)).values()); + return getSecondaryResources(ResourceID.fromResource(primary)); } - protected UpdatableCache initCache() { - return new ConcurrentHashMapCache<>(); + public Set getSecondaryResources(ResourceID primaryID) { + var cachedValues = cache.get(primaryID); + if (cachedValues == null) { + return Collections.emptySet(); + } else { + return new HashSet<>(cache.get(primaryID).values()); + } + } + + public Optional getSecondaryResource(ResourceID primaryID) { + var resources = getSecondaryResources(primaryID); + if (resources.isEmpty()) { + return Optional.empty(); + } else if (resources.size() == 1) { + return Optional.of(resources.iterator().next()); + } else { + throw new IllegalStateException("More than 1 secondary resource related to primary"); + } } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractResourceEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractResourceEventSource.java index 2d568f7a5b..051a75ff20 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractResourceEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractResourceEventSource.java @@ -2,7 +2,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; -public abstract class AbstractResourceEventSource +public abstract class AbstractResourceEventSource extends AbstractEventSource implements ResourceEventSource { private final Class resourceClass; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CachingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CachingEventSource.java index 141abb8816..55bd1ab920 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CachingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CachingEventSource.java @@ -14,7 +14,7 @@ * @param represents the type of resources (usually external non-kubernetes ones) being handled. */ public abstract class CachingEventSource - extends AbstractResourceEventSource implements Cache { + extends AbstractResourceEventSource implements Cache { protected UpdatableCache cache; @@ -46,5 +46,6 @@ public Stream list(Predicate predicate) { public Optional getCachedValue(ResourceID resourceID) { return cache.get(resourceID); } + protected abstract UpdatableCache initCache(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java index 62f7928763..21abea8a62 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java @@ -2,5 +2,16 @@ import java.util.function.Function; -public interface IDMapper extends Function { +public interface IDMapper extends Function { + + /** + * If a polling event source handles only single secondary resources + * + * @return static id mapper, all resources are mapped for same id + * @param + */ + static IDMapper singleResourceIDMapper() { + return r -> "id"; + } + } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSource.java index c3ffc326bf..0c0f0c6b8e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSource.java @@ -1,6 +1,7 @@ package io.javaoperatorsdk.operator.processing.event.source.controller; import java.util.Optional; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -94,4 +95,9 @@ public void onDelete(T resource, boolean b) { public Optional getSecondaryResource(T primary) { throw new IllegalStateException("This method should not be called here. Primary: " + primary); } + + @Override + public Set getSecondaryResources(T primary) { + throw new IllegalStateException("This method should not be called here. Primary: " + primary); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java index 723dc4aeba..1c89dabbf3 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java @@ -1,5 +1,7 @@ package io.javaoperatorsdk.operator.processing.event.source.inbound; +import java.util.Set; + import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.ResourceID; @@ -12,12 +14,16 @@ public CachingInboundEventSource(Class resourceClass, IDMapper idProvider) super(resourceClass, idProvider); } - public void handleResourceEvent(R resource, ResourceID primaryID) { - super.handleEvent(resource, primaryID); + public void handleResourceEvent(ResourceID primaryID, Set resources) { + super.handleResourcesUpdate(primaryID, resources); + } + + public void handleResourceEvent(ResourceID primaryID, R resource) { + super.handleResourcesUpdate(primaryID, resource); } - public void handleResourceDeleteEvent(ResourceID primaryID,String resourceID) { - super.handleDelete(primaryID,resourceID); + public void handleResourceDeleteEvent(ResourceID primaryID, String resourceID) { + super.handleDelete(primaryID, Set.of(resourceID)); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java index f4a6529aa2..5d1d12875f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.processing.event.source.informer; -import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java index 60fa62f71a..60aa480542 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java @@ -2,11 +2,8 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; import java.util.function.Predicate; -import java.util.stream.Collectors; -import io.javaoperatorsdk.operator.processing.event.source.IDMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,6 +13,7 @@ import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.Cache; import io.javaoperatorsdk.operator.processing.event.source.CachingEventSource; +import io.javaoperatorsdk.operator.processing.event.source.IDMapper; import io.javaoperatorsdk.operator.processing.event.source.ResourceEventAware; /** @@ -43,14 +41,20 @@ public class PerResourcePollingEventSource private final long period; public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, - Cache

resourceCache, long period, Class resourceClass, IDMapper idProvider) { + Cache

resourceCache, long period, Class resourceClass) { + this(resourceFetcher, resourceCache, period, null, resourceClass, + IDMapper.singleResourceIDMapper()); + } + + public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, + Cache

resourceCache, long period, Class resourceClass, IDMapper idProvider) { this(resourceFetcher, resourceCache, period, null, resourceClass, idProvider); } public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, - Cache

resourceCache, long period, - Predicate

registerPredicate, Class resourceClass, - IDMapper idProvider) { + Cache

resourceCache, long period, + Predicate

registerPredicate, Class resourceClass, + IDMapper idProvider) { super(resourceClass, idProvider); this.resourceFetcher = resourceFetcher; this.resourceCache = resourceCache; @@ -58,28 +62,10 @@ public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, this.registerPredicate = registerPredicate; } - private void pollForResource(P primary) { - var primaryID = ResourceID.fromResource(primary); - var value = resourceFetcher.fetchResources(primary); - var newResourceIDs = value.stream().map(idProvider::apply).collect(Collectors.toSet()); - var cachedValues = cache.get(primaryID); - - Set toDelete = cachedValues.keySet().stream().filter(r -> !newResourceIDs.contains(r)) - .collect(Collectors.toSet()); - - toDelete.forEach(resourceID -> handleDelete(primaryID,resourceID)); - value.forEach(v->super.handleEvent(v,primaryID)); - } - - private Set getAndCacheResource(ResourceID resourceID) { - var resource = resourceCache.get(resourceID); - if (resource.isPresent()) { - var values = resourceFetcher.fetchResources(resource.get()); - values.forEach(r-> handleEvent(r,resourceID)); - return values; - } else { - return Collections.emptySet(); - } + private Set getAndCacheResource(P primary) { + var values = resourceFetcher.fetchResources(primary); + handleResourcesUpdate(ResourceID.fromResource(primary), values); + return values; } @Override @@ -100,7 +86,7 @@ public void onResourceDeleted(P resource) { log.debug("Canceling task for resource: {}", resource); task.cancel(); } - cache.remove(resourceID); + handleDelete(resourceID); } // This method is always called from the same Thread for the same resource, @@ -122,7 +108,7 @@ public void run() { // always use up-to-date resource from cache var res = resourceCache.get(resourceID); res.ifPresentOrElse( - PerResourcePollingEventSource.this::pollForResource, + PerResourcePollingEventSource.this::getAndCacheResource, () -> log.warn("No resource in cache for resource ID: {}", resourceID)); } }; @@ -140,22 +126,22 @@ public void run() { */ @Override public Set getSecondaryResources(P primary) { - return getValueFromCacheOrSupplier(ResourceID.fromResource(primary)); + return getValueFromCacheOrSupplier(primary); } /** * - * @param resourceID of the target related resource + * @param primary of the target related resource * @return the cached value of the resource, if not present it gets the resource from the * supplier. The value provided from the supplier is cached, but no new event is * propagated. */ - public Set getValueFromCacheOrSupplier(ResourceID resourceID) { - var cachedValue = cache.get(resourceID); - if (cachedValue != null) { + public Set getValueFromCacheOrSupplier(P primary) { + var cachedValue = cache.get(ResourceID.fromResource(primary)); + if (cachedValue != null && !cachedValue.isEmpty()) { return new HashSet<>(cachedValue.values()); } else { - return getAndCacheResource(resourceID); + return getAndCacheResource(primary); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java index b2d684f311..ea4f6ac48b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java @@ -1,11 +1,11 @@ package io.javaoperatorsdk.operator.processing.event.source.polling; -import java.util.*; -import java.util.function.Function; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; import java.util.function.Supplier; -import java.util.stream.Collectors; -import io.javaoperatorsdk.operator.processing.event.source.IDProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -13,6 +13,7 @@ import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.IDMapper; /** * Polls resource (on contrary to {@link PerResourcePollingEventSource}) not per resource bases but @@ -22,19 +23,19 @@ * not contain the target resource it means it is not created yet or was deleted while an operator * was not running. * - *

Another caveat with this is if the cached object is checked in the reconciler and created - * since not in the cache it should be manually added to the cache, since it can happen that the + *

+ * Another caveat with this is if the cached object is checked in the reconciler and created since + * not in the cache it should be manually added to the cache, since it can happen that the * reconciler is triggered before the cache is propagated with the new resource from a scheduled - * execution. See {@link #put(ResourceID, Object)} method. So the generic workflow in reconciler - * should be: + * execution. See {@link #handleRecentResourceCreate(ResourceID, Object)} and update method. So the + * generic workflow in reconciler should be: * *

    - *
  • Check if the cache contains the resource. - *
  • If cache contains the resource reconcile it - compare with target state, update if - * necessary - *
  • if cache not contains the resource create it. - *
  • If the resource was created or updated, put the new version of the resource manually to the - * cache. + *
  • Check if the cache contains the resource. + *
  • If cache contains the resource reconcile it - compare with target state, update if necessary + *
  • if cache not contains the resource create it. + *
  • If the resource was created or updated, put the new version of the resource manually to the + * cache. *
* * @param type of the polled resource @@ -49,12 +50,21 @@ public class PollingEventSource private final Supplier>> supplierToPoll; private final long period; + public PollingEventSource( + Supplier>> supplier, + long period, + Class resourceClass) { + super(resourceClass, IDMapper.singleResourceIDMapper()); + this.supplierToPoll = supplier; + this.period = period; + } + public PollingEventSource( Supplier>> supplier, long period, Class resourceClass, - Function idProvider) { - super(resourceClass, idProvider); + IDMapper idMapper) { + super(resourceClass, idMapper); this.supplierToPoll = supplier; this.period = period; } @@ -78,29 +88,9 @@ public void run() { period); } - // todo can be optimized; protected synchronized void getStateAndFillCache() { var values = supplierToPoll.get(); - HashMap toDelete = new HashMap<>(); - - cache.forEach( - (primaryID, resourcesMap) -> { - var newIds = - values.get(primaryID).stream() - .map(idProvider::apply) - .collect(Collectors.toSet()); - resourcesMap.forEach( - (actualID, actualResource) -> { - if (!newIds.contains(actualID)) { - toDelete.put(primaryID,actualID); - } - }); - }); - toDelete.forEach(super::handleDelete); - - - values.forEach( - (k, v) -> v.forEach(r -> handleEvent(r, k))); + values.forEach(this::handleResourcesUpdate); } @Override diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java index b8ba8d9b6a..73f473f80f 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java @@ -7,6 +7,7 @@ import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.EventHandler; import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; +import io.javaoperatorsdk.operator.processing.event.ResourceID; import static io.javaoperatorsdk.operator.processing.event.source.SampleExternalResource.*; import static org.assertj.core.api.Assertions.assertThat; @@ -15,61 +16,63 @@ class ExternalResourceCachingEventSourceTest extends AbstractEventSourceTestBase, EventHandler> { + private ResourceID primary = new ResourceID("test1", "default"); + @BeforeEach public void setup() { - setUpSource(new SimpleExternalCachingEventSource()); + setUpSource(new TestExternalCachingEventSource()); } @Test public void putsNewResourceIntoCacheAndProducesEvent() { - source.handleEvent(testResource1(), testResource1ID()); + source.handleResourcesUpdate(primaryID1(), testResource1()); - verify(eventHandler, times(1)).handleEvent(eq(new Event(testResource1ID()))); - assertThat(source.getCachedValue(testResource1ID())).isPresent(); + verify(eventHandler, times(1)).handleEvent(eq(new Event(primaryID1()))); + assertThat(source.getSecondaryResource(primaryID1())).isPresent(); } @Test public void propagatesEventIfResourceChanged() { var res2 = testResource1(); res2.setValue("changedValue"); - source.handleEvent(testResource1(), testResource1ID()); - source.handleEvent(res2, testResource1ID()); + source.handleResourcesUpdate(primaryID1(), testResource1()); + source.handleResourcesUpdate(primaryID1(), res2); - verify(eventHandler, times(2)).handleEvent(eq(new Event(testResource1ID()))); - assertThat(source.getCachedValue(testResource1ID()).get()).isEqualTo(res2); + verify(eventHandler, times(2)).handleEvent(eq(new Event(primaryID1()))); + assertThat(source.getSecondaryResource(primaryID1()).get()).isEqualTo(res2); } @Test public void noEventPropagatedIfTheResourceIsNotChanged() { - source.handleEvent(testResource1(), testResource1ID()); - source.handleEvent(testResource1(), testResource1ID()); + source.handleResourcesUpdate(primaryID1(), testResource1()); + source.handleResourcesUpdate(primaryID1(), testResource1()); - verify(eventHandler, times(1)).handleEvent(eq(new Event(testResource1ID()))); - assertThat(source.getCachedValue(testResource1ID())).isPresent(); + verify(eventHandler, times(1)).handleEvent(eq(new Event(primaryID1()))); + assertThat(source.getSecondaryResource(primaryID1())).isPresent(); } @Test public void propagatesEventOnDeleteIfThereIsPrevResourceInCache() { - source.handleEvent(testResource1(), testResource1ID()); - source.handleDelete(testResource1ID()); + source.handleResourcesUpdate(primaryID1(), testResource1()); + source.handleDelete(primaryID1()); - verify(eventHandler, times(2)).handleEvent(eq(new Event(testResource1ID()))); - assertThat(source.getCachedValue(testResource1ID())).isNotPresent(); + verify(eventHandler, times(2)).handleEvent(eq(new Event(primaryID1()))); + assertThat(source.getSecondaryResource(primaryID1())).isNotPresent(); } @Test public void noEventOnDeleteIfResourceWasNotInCacheBefore() { - source.handleDelete(testResource1ID()); + source.handleDelete(primaryID1()); - verify(eventHandler, times(0)).handleEvent(eq(new Event(testResource1ID()))); + verify(eventHandler, times(0)).handleEvent(eq(new Event(primaryID1()))); } - public static class SimpleExternalCachingEventSource + public static class TestExternalCachingEventSource extends ExternalResourceCachingEventSource { - public SimpleExternalCachingEventSource() { - super(SampleExternalResource.class, idProvider); + public TestExternalCachingEventSource() { + super(SampleExternalResource.class, (r) -> r.getName() + "#" + r.getValue()); } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/SampleExternalResource.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/SampleExternalResource.java index bf2ff42c96..73258adbee 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/SampleExternalResource.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/SampleExternalResource.java @@ -20,7 +20,7 @@ public static SampleExternalResource testResource2() { return new SampleExternalResource(NAME_2, DEFAULT_VALUE_2); } - public static ResourceID testResource1ID() { + public static ResourceID primaryID1() { return new ResourceID(NAME_1, "testns"); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java index 71475ee3c2..d31109af0e 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java @@ -1,5 +1,6 @@ package io.javaoperatorsdk.operator.processing.event.source.polling; +import java.util.Collections; import java.util.Optional; import java.util.Set; @@ -8,9 +9,9 @@ import io.javaoperatorsdk.operator.TestUtils; import io.javaoperatorsdk.operator.processing.event.EventHandler; -import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.AbstractEventSourceTestBase; import io.javaoperatorsdk.operator.processing.event.source.Cache; +import io.javaoperatorsdk.operator.processing.event.source.IDMapper; import io.javaoperatorsdk.operator.processing.event.source.SampleExternalResource; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; @@ -40,7 +41,7 @@ public void setup() { .thenReturn(Set.of(SampleExternalResource.testResource1())); setUpSource(new PerResourcePollingEventSource<>(supplier, resourceCache, PERIOD, - SampleExternalResource.class,)); + SampleExternalResource.class)); } @Test @@ -56,7 +57,7 @@ public void pollsTheResourceAfterAwareOfIt() throws InterruptedException { public void registeringTaskOnAPredicate() throws InterruptedException { setUpSource(new PerResourcePollingEventSource<>(supplier, resourceCache, PERIOD, testCustomResource -> testCustomResource.getMetadata().getGeneration() > 1, - SampleExternalResource.class,)); + SampleExternalResource.class, IDMapper.singleResourceIDMapper())); source.onResourceCreated(testCustomResource); Thread.sleep(2 * PERIOD); @@ -73,8 +74,8 @@ public void registeringTaskOnAPredicate() throws InterruptedException { public void propagateEventOnDeletedResource() throws InterruptedException { source.onResourceCreated(testCustomResource); when(supplier.fetchResources(any())) - .thenReturn(Optional.of(SampleExternalResource.testResource1())) - .thenReturn(Optional.empty()); + .thenReturn(Set.of(SampleExternalResource.testResource1())) + .thenReturn(Collections.emptySet()); Thread.sleep(3 * PERIOD); verify(supplier, atLeast(2)).fetchResources(eq(testCustomResource)); @@ -85,16 +86,16 @@ public void propagateEventOnDeletedResource() throws InterruptedException { public void getsValueFromCacheOrSupplier() throws InterruptedException { source.onResourceCreated(testCustomResource); when(supplier.fetchResources(any())) - .thenReturn(Optional.empty()) - .thenReturn(Optional.of(SampleExternalResource.testResource1())); + .thenReturn(Collections.emptySet()) + .thenReturn(Set.of(SampleExternalResource.testResource1())); Thread.sleep(PERIOD / 2); - var value = source.getValueFromCacheOrSupplier(ResourceID.fromResource(testCustomResource)); + var value = source.getValueFromCacheOrSupplier(testCustomResource); Thread.sleep(PERIOD * 2); - assertThat(value).isPresent(); + assertThat(value).hasSize(1); verify(eventHandler, never()).handleEvent(any()); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java index d855c42342..8e823ff367 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java @@ -24,7 +24,7 @@ class PollingEventSourceTest private Supplier>> supplier = mock(Supplier.class); private final PollingEventSource pollingEventSource = new PollingEventSource<>(supplier, 50L, SampleExternalResource.class, - (SampleExternalResource er) -> er.getName()+"#"+er.getValue()); + (SampleExternalResource er) -> er.getName() + "#" + er.getValue()); @BeforeEach public void setup() { @@ -61,13 +61,13 @@ public void doesNotPropagateEventIfResourceNotChanged() throws InterruptedExcept private Map> testResponseWithOneValue() { Map> res = new HashMap<>(); - res.put(testResource1ID(), Set.of(testResource1())); + res.put(primaryID1(), Set.of(testResource1())); return res; } private Map> testResponseWithTwoValues() { Map> res = new HashMap<>(); - res.put(testResource1ID(), Set.of(testResource1())); + res.put(primaryID1(), Set.of(testResource1())); res.put(testResource2ID(), Set.of(testResource2())); return res; } diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java index 013c5c2d8d..5bef4adb04 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java @@ -91,14 +91,10 @@ public static String decode(String value) { public Set fetchResources(MySQLSchema primaryResource) { try (Connection connection = getConnection()) { return SchemaService.getSchema(connection, primaryResource.getMetadata().getName()) - .map(Set::of).orElse(Collections.emptySet()); + .map(Set::of).orElse(Collections.emptySet()); } catch (SQLException e) { throw new RuntimeException("Error while trying read Schema", e); } } - @Override - public String getID(Schema resource) { - return resource.getName(); - } } diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaPollingResourceFetcher.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaPollingResourceFetcher.java index 6c1f3bab85..1470d46e49 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaPollingResourceFetcher.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaPollingResourceFetcher.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.sample.dependent; -import java.util.Optional; import java.util.Set; import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingEventSource; From 75ea2294c67f5e7bbe3174e01c0ba40cc7aa8604 Mon Sep 17 00:00:00 2001 From: csviri Date: Thu, 21 Apr 2022 14:05:00 +0200 Subject: [PATCH 04/18] fix: tests --- .../AbstractCachingDependentResource.java | 2 +- .../PerResourcePollingDependentResource.java | 2 +- .../external/PollingDependentResource.java | 2 +- .../ExternalResourceCachingEventSource.java | 33 ++++++++++-- .../inbound/CachingInboundEventSource.java | 2 +- .../PerResourcePollingEventSource.java | 50 +++++++++---------- .../source/polling/PollingEventSource.java | 4 +- ...xternalResourceCachingEventSourceTest.java | 1 - .../event/source/SampleExternalResource.java | 2 +- .../PerResourcePollingEventSourceTest.java | 44 +++++++++++++--- .../polling/PollingEventSourceTest.java | 8 +-- 11 files changed, 101 insertions(+), 49 deletions(-) rename operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/{ => source}/ExternalResourceCachingEventSource.java (74%) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java index 1b2d388088..4f85cb9d1d 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractCachingDependentResource.java @@ -4,7 +4,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.dependent.AbstractEventSourceHolderDependentResource; -import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; +import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; public abstract class AbstractCachingDependentResource extends diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java index ee9e1b54be..1afa566a3a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java @@ -2,7 +2,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; -import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; +import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingEventSource; public abstract class PerResourcePollingDependentResource diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java index 61460db1bf..a47bfe4070 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java @@ -6,8 +6,8 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; -import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.source.IDMapper; import io.javaoperatorsdk.operator.processing.event.source.polling.PollingEventSource; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ExternalResourceCachingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java similarity index 74% rename from operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ExternalResourceCachingEventSource.java rename to operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java index 8a872846f6..dd7dca67ad 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ExternalResourceCachingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java @@ -1,16 +1,22 @@ -package io.javaoperatorsdk.operator.processing.event; +package io.javaoperatorsdk.operator.processing.event.source; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationCacheFiller; -import io.javaoperatorsdk.operator.processing.event.source.*; +import io.javaoperatorsdk.operator.processing.event.Event; +import io.javaoperatorsdk.operator.processing.event.ResourceID; public abstract class ExternalResourceCachingEventSource extends AbstractResourceEventSource implements RecentOperationCacheFiller { + private static Logger log = LoggerFactory.getLogger(ExternalResourceCachingEventSource.class); + protected final IDMapper idMapper; protected Map> cache = new ConcurrentHashMap<>(); @@ -21,7 +27,10 @@ protected ExternalResourceCachingEventSource(Class resourceClass, IDMapper } public synchronized void handleDelete(ResourceID primaryID) { - cache.remove(primaryID); + var res = cache.remove(primaryID); + if (res != null) { + getEventHandler().handleEvent(new Event(primaryID)); + } } public synchronized void handleDelete(ResourceID primaryID, String resourceID) { @@ -49,17 +58,31 @@ public synchronized void handleDelete(ResourceID primaryID, Set resource } public synchronized void handleResourcesUpdate(ResourceID primaryID, R actualResource) { - handleResourcesUpdate(primaryID, Set.of(actualResource)); + handleResourcesUpdate(primaryID, Set.of(actualResource), true); } public synchronized void handleResourcesUpdate(ResourceID primaryID, Set newResources) { + handleResourcesUpdate(primaryID, newResources, true); + } + + public synchronized void handleResourcesUpdate(Map> allNewResources) { + var toDelete = cache.keySet().stream().filter(k -> !allNewResources.containsKey(k)) + .collect(Collectors.toList()); + toDelete.forEach(this::handleDelete); + allNewResources.forEach((primaryID, resources) -> handleResourcesUpdate(primaryID, resources)); + } + + public synchronized void handleResourcesUpdate(ResourceID primaryID, Set newResources, + boolean propagateEvent) { + log.debug("Handling resources update for: {} numberOfResources: {} ", primaryID, + newResources.size()); if (!isRunning()) { return; } var cachedResources = cache.get(primaryID); var newResourcesMap = newResources.stream().collect(Collectors.toMap(idMapper, r -> r)); cache.put(primaryID, newResourcesMap); - if (!newResourcesMap.equals(cachedResources)) { + if (propagateEvent && !newResourcesMap.equals(cachedResources)) { getEventHandler().handleEvent(new Event(primaryID)); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java index 1c89dabbf3..f2f08baeb4 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java @@ -3,8 +3,8 @@ import java.util.Set; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.source.IDMapper; public class CachingInboundEventSource diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java index 60aa480542..8bd5530f6d 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java @@ -9,10 +9,10 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.OperatorException; -import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.Cache; import io.javaoperatorsdk.operator.processing.event.source.CachingEventSource; +import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.source.IDMapper; import io.javaoperatorsdk.operator.processing.event.source.ResourceEventAware; @@ -39,6 +39,7 @@ public class PerResourcePollingEventSource private final Cache

resourceCache; private final Predicate

registerPredicate; private final long period; + private final Set fetchedForPrimaries = ConcurrentHashMap.newKeySet(); public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, Cache

resourceCache, long period, Class resourceClass) { @@ -62,9 +63,10 @@ public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, this.registerPredicate = registerPredicate; } - private Set getAndCacheResource(P primary) { + private synchronized Set getAndCacheResource(P primary, boolean fromGetter) { var values = resourceFetcher.fetchResources(primary); - handleResourcesUpdate(ResourceID.fromResource(primary), values); + handleResourcesUpdate(ResourceID.fromResource(primary), values, !fromGetter); + fetchedForPrimaries.add(ResourceID.fromResource(primary)); return values; } @@ -87,6 +89,7 @@ public void onResourceDeleted(P resource) { task.cancel(); } handleDelete(resourceID); + fetchedForPrimaries.remove(resourceID); } // This method is always called from the same Thread for the same resource, @@ -94,26 +97,27 @@ public void onResourceDeleted(P resource) { // important // because otherwise there will be a race condition related to the timerTasks. private void checkAndRegisterTask(P resource) { - var resourceID = ResourceID.fromResource(resource); - if (timerTasks.get(resourceID) == null && (registerPredicate == null + var primaryID = ResourceID.fromResource(resource); + if (timerTasks.get(primaryID) == null && (registerPredicate == null || registerPredicate.test(resource))) { var task = new TimerTask() { @Override public void run() { if (!isRunning()) { - log.debug("Event source not yet started. Will not run for: {}", resourceID); + log.debug("Event source not yet started. Will not run for: {}", primaryID); return; } // always use up-to-date resource from cache - var res = resourceCache.get(resourceID); - res.ifPresentOrElse( - PerResourcePollingEventSource.this::getAndCacheResource, - () -> log.warn("No resource in cache for resource ID: {}", resourceID)); + var res = resourceCache.get(primaryID); + res.ifPresentOrElse(p -> getAndCacheResource(p, false), + () -> log.warn("No resource in cache for resource ID: {}", primaryID)); } }; - timerTasks.put(resourceID, task); - timer.schedule(task, 0, period); + timerTasks.put(primaryID, task); + // there is a delay, to not do two fetches when the resources first appeared + // and getSecondaryResource is called on reconciliation. + timer.schedule(task, period, period); } } @@ -126,22 +130,18 @@ public void run() { */ @Override public Set getSecondaryResources(P primary) { - return getValueFromCacheOrSupplier(primary); - } - - /** - * - * @param primary of the target related resource - * @return the cached value of the resource, if not present it gets the resource from the - * supplier. The value provided from the supplier is cached, but no new event is - * propagated. - */ - public Set getValueFromCacheOrSupplier(P primary) { - var cachedValue = cache.get(ResourceID.fromResource(primary)); + var primaryID = ResourceID.fromResource(primary); + var cachedValue = cache.get(primaryID); if (cachedValue != null && !cachedValue.isEmpty()) { return new HashSet<>(cachedValue.values()); } else { - return getAndCacheResource(primary); + synchronized (this) { + if (fetchedForPrimaries.contains(primaryID)) { + return Collections.emptySet(); + } else { + return getAndCacheResource(primary, true); + } + } } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java index ea4f6ac48b..ab54234c2c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java @@ -11,8 +11,8 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.OperatorException; -import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.source.IDMapper; /** @@ -90,7 +90,7 @@ public void run() { protected synchronized void getStateAndFillCache() { var values = supplierToPoll.get(); - values.forEach(this::handleResourcesUpdate); + handleResourcesUpdate(values); } @Override diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java index 73f473f80f..c54f8e6163 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java @@ -6,7 +6,6 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.EventHandler; -import io.javaoperatorsdk.operator.processing.event.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.ResourceID; import static io.javaoperatorsdk.operator.processing.event.source.SampleExternalResource.*; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/SampleExternalResource.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/SampleExternalResource.java index 73258adbee..6f45090b35 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/SampleExternalResource.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/SampleExternalResource.java @@ -24,7 +24,7 @@ public static ResourceID primaryID1() { return new ResourceID(NAME_1, "testns"); } - public static ResourceID testResource2ID() { + public static ResourceID primaryID2() { return new ResourceID(NAME_2, "testns"); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java index d31109af0e..58ef00b3a4 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java @@ -41,11 +41,11 @@ public void setup() { .thenReturn(Set.of(SampleExternalResource.testResource1())); setUpSource(new PerResourcePollingEventSource<>(supplier, resourceCache, PERIOD, - SampleExternalResource.class)); + SampleExternalResource.class, r -> r.getName() + "#" + r.getValue())); } @Test - public void pollsTheResourceAfterAwareOfIt() throws InterruptedException { + void pollsTheResourceAfterAwareOfIt() throws InterruptedException { source.onResourceCreated(testCustomResource); Thread.sleep(3 * PERIOD); @@ -54,7 +54,7 @@ public void pollsTheResourceAfterAwareOfIt() throws InterruptedException { } @Test - public void registeringTaskOnAPredicate() throws InterruptedException { + void registeringTaskOnAPredicate() throws InterruptedException { setUpSource(new PerResourcePollingEventSource<>(supplier, resourceCache, PERIOD, testCustomResource -> testCustomResource.getMetadata().getGeneration() > 1, SampleExternalResource.class, IDMapper.singleResourceIDMapper())); @@ -71,7 +71,7 @@ public void registeringTaskOnAPredicate() throws InterruptedException { } @Test - public void propagateEventOnDeletedResource() throws InterruptedException { + void propagateEventOnDeletedResource() throws InterruptedException { source.onResourceCreated(testCustomResource); when(supplier.fetchResources(any())) .thenReturn(Set.of(SampleExternalResource.testResource1())) @@ -83,7 +83,34 @@ public void propagateEventOnDeletedResource() throws InterruptedException { } @Test - public void getsValueFromCacheOrSupplier() throws InterruptedException { + void getSecondaryResourceInitiatesFetchJustForFirstTime() throws InterruptedException { + source.onResourceCreated(testCustomResource); + when(supplier.fetchResources(any())) + .thenReturn(Set.of(SampleExternalResource.testResource1())) + .thenReturn( + Set.of(SampleExternalResource.testResource1(), SampleExternalResource.testResource2())); + + var value = source.getSecondaryResources(testCustomResource); + + verify(supplier, times(1)).fetchResources(eq(testCustomResource)); + verify(eventHandler, never()).handleEvent(any()); + assertThat(value).hasSize(1); + + value = source.getSecondaryResources(testCustomResource); + + assertThat(value).hasSize(1); + verify(supplier, times(1)).fetchResources(eq(testCustomResource)); + verify(eventHandler, never()).handleEvent(any()); + + Thread.sleep(PERIOD * 2); + + verify(supplier, atLeast(2)).fetchResources(eq(testCustomResource)); + value = source.getSecondaryResources(testCustomResource); + assertThat(value).hasSize(2); + } + + @Test + void getsValueFromCacheOrSupplier() throws InterruptedException { source.onResourceCreated(testCustomResource); when(supplier.fetchResources(any())) .thenReturn(Collections.emptySet()) @@ -91,12 +118,15 @@ public void getsValueFromCacheOrSupplier() throws InterruptedException { Thread.sleep(PERIOD / 2); - var value = source.getValueFromCacheOrSupplier(testCustomResource); + var value = source.getSecondaryResources(testCustomResource); + verify(eventHandler, times(0)).handleEvent(any()); + assertThat(value).hasSize(0); Thread.sleep(PERIOD * 2); + value = source.getSecondaryResources(testCustomResource); assertThat(value).hasSize(1); - verify(eventHandler, never()).handleEvent(any()); + verify(eventHandler, times(1)).handleEvent(any()); } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java index 8e823ff367..03def1c913 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java @@ -32,7 +32,7 @@ public void setup() { } @Test - public void pollsAndProcessesEvents() throws InterruptedException { + void pollsAndProcessesEvents() throws InterruptedException { when(supplier.get()).thenReturn(testResponseWithTwoValues()); pollingEventSource.start(); Thread.sleep(100); @@ -41,7 +41,7 @@ public void pollsAndProcessesEvents() throws InterruptedException { } @Test - public void propagatesEventForRemovedResources() throws InterruptedException { + void propagatesEventForRemovedResources() throws InterruptedException { when(supplier.get()).thenReturn(testResponseWithTwoValues()) .thenReturn(testResponseWithOneValue()); pollingEventSource.start(); @@ -51,7 +51,7 @@ public void propagatesEventForRemovedResources() throws InterruptedException { } @Test - public void doesNotPropagateEventIfResourceNotChanged() throws InterruptedException { + void doesNotPropagateEventIfResourceNotChanged() throws InterruptedException { when(supplier.get()).thenReturn(testResponseWithTwoValues()); pollingEventSource.start(); Thread.sleep(250); @@ -68,7 +68,7 @@ private Map> testResponseWithOneValue() private Map> testResponseWithTwoValues() { Map> res = new HashMap<>(); res.put(primaryID1(), Set.of(testResource1())); - res.put(testResource2ID(), Set.of(testResource2())); + res.put(primaryID2(), Set.of(testResource2())); return res; } From abb2758e7b2073bebec1ac628b880a6b8bb6a206 Mon Sep 17 00:00:00 2001 From: csviri Date: Thu, 21 Apr 2022 14:26:40 +0200 Subject: [PATCH 05/18] test improvements --- .../ExternalResourceCachingEventSource.java | 20 +++-- .../PerResourcePollingEventSource.java | 12 ++- ...xternalResourceCachingEventSourceTest.java | 82 +++++++++++++++---- 3 files changed, 84 insertions(+), 30 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java index dd7dca67ad..b5a3443d48 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java @@ -26,22 +26,26 @@ protected ExternalResourceCachingEventSource(Class resourceClass, IDMapper this.idMapper = idMapper; } - public synchronized void handleDelete(ResourceID primaryID) { + protected synchronized void handleDelete(ResourceID primaryID) { var res = cache.remove(primaryID); if (res != null) { getEventHandler().handleEvent(new Event(primaryID)); } } - public synchronized void handleDelete(ResourceID primaryID, String resourceID) { + protected synchronized void handleDelete(ResourceID primaryID, String resourceID) { handleDelete(primaryID, Set.of(resourceID)); } - public synchronized void handleDelete(ResourceID primaryID, R resource) { + protected synchronized void handleDeleteResources(ResourceID primaryID, Set resource) { + handleDelete(primaryID, resource.stream().map(idMapper).collect(Collectors.toSet())); + } + + protected synchronized void handleDelete(ResourceID primaryID, R resource) { handleDelete(primaryID, Set.of(idMapper.apply(resource))); } - public synchronized void handleDelete(ResourceID primaryID, Set resourceID) { + protected synchronized void handleDelete(ResourceID primaryID, Set resourceID) { if (!isRunning()) { return; } @@ -57,22 +61,22 @@ public synchronized void handleDelete(ResourceID primaryID, Set resource } } - public synchronized void handleResourcesUpdate(ResourceID primaryID, R actualResource) { + protected synchronized void handleResourcesUpdate(ResourceID primaryID, R actualResource) { handleResourcesUpdate(primaryID, Set.of(actualResource), true); } - public synchronized void handleResourcesUpdate(ResourceID primaryID, Set newResources) { + protected synchronized void handleResourcesUpdate(ResourceID primaryID, Set newResources) { handleResourcesUpdate(primaryID, newResources, true); } - public synchronized void handleResourcesUpdate(Map> allNewResources) { + protected synchronized void handleResourcesUpdate(Map> allNewResources) { var toDelete = cache.keySet().stream().filter(k -> !allNewResources.containsKey(k)) .collect(Collectors.toList()); toDelete.forEach(this::handleDelete); allNewResources.forEach((primaryID, resources) -> handleResourcesUpdate(primaryID, resources)); } - public synchronized void handleResourcesUpdate(ResourceID primaryID, Set newResources, + protected synchronized void handleResourcesUpdate(ResourceID primaryID, Set newResources, boolean propagateEvent) { log.debug("Handling resources update for: {} numberOfResources: {} ", primaryID, newResources.size()); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java index 8bd5530f6d..cd837fcde4 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java @@ -63,7 +63,7 @@ public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, this.registerPredicate = registerPredicate; } - private synchronized Set getAndCacheResource(P primary, boolean fromGetter) { + private Set getAndCacheResource(P primary, boolean fromGetter) { var values = resourceFetcher.fetchResources(primary); handleResourcesUpdate(ResourceID.fromResource(primary), values, !fromGetter); fetchedForPrimaries.add(ResourceID.fromResource(primary)); @@ -135,12 +135,10 @@ public Set getSecondaryResources(P primary) { if (cachedValue != null && !cachedValue.isEmpty()) { return new HashSet<>(cachedValue.values()); } else { - synchronized (this) { - if (fetchedForPrimaries.contains(primaryID)) { - return Collections.emptySet(); - } else { - return getAndCacheResource(primary, true); - } + if (fetchedForPrimaries.contains(primaryID)) { + return Collections.emptySet(); + } else { + return getAndCacheResource(primary, true); } } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java index c54f8e6163..6f330f8948 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java @@ -1,12 +1,13 @@ package io.javaoperatorsdk.operator.processing.event.source; +import java.util.Set; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.EventHandler; -import io.javaoperatorsdk.operator.processing.event.ResourceID; import static io.javaoperatorsdk.operator.processing.event.source.SampleExternalResource.*; import static org.assertj.core.api.Assertions.assertThat; @@ -15,58 +16,109 @@ class ExternalResourceCachingEventSourceTest extends AbstractEventSourceTestBase, EventHandler> { - private ResourceID primary = new ResourceID("test1", "default"); - @BeforeEach public void setup() { setUpSource(new TestExternalCachingEventSource()); } @Test - public void putsNewResourceIntoCacheAndProducesEvent() { + void putsNewResourceIntoCacheAndProducesEvent() { source.handleResourcesUpdate(primaryID1(), testResource1()); - verify(eventHandler, times(1)).handleEvent(eq(new Event(primaryID1()))); + verify(eventHandler, times(1)).handleEvent(new Event(primaryID1())); assertThat(source.getSecondaryResource(primaryID1())).isPresent(); } @Test - public void propagatesEventIfResourceChanged() { + void propagatesEventIfResourceChanged() { var res2 = testResource1(); res2.setValue("changedValue"); source.handleResourcesUpdate(primaryID1(), testResource1()); source.handleResourcesUpdate(primaryID1(), res2); - - verify(eventHandler, times(2)).handleEvent(eq(new Event(primaryID1()))); - assertThat(source.getSecondaryResource(primaryID1()).get()).isEqualTo(res2); + verify(eventHandler, times(2)).handleEvent(new Event(primaryID1())); + assertThat(source.getSecondaryResource(primaryID1())).contains(res2); } @Test - public void noEventPropagatedIfTheResourceIsNotChanged() { + void noEventPropagatedIfTheResourceIsNotChanged() { source.handleResourcesUpdate(primaryID1(), testResource1()); source.handleResourcesUpdate(primaryID1(), testResource1()); - verify(eventHandler, times(1)).handleEvent(eq(new Event(primaryID1()))); + verify(eventHandler, times(1)).handleEvent(new Event(primaryID1())); assertThat(source.getSecondaryResource(primaryID1())).isPresent(); } @Test - public void propagatesEventOnDeleteIfThereIsPrevResourceInCache() { + void propagatesEventOnDeleteIfThereIsPrevResourceInCache() { source.handleResourcesUpdate(primaryID1(), testResource1()); source.handleDelete(primaryID1()); - verify(eventHandler, times(2)).handleEvent(eq(new Event(primaryID1()))); + verify(eventHandler, times(2)).handleEvent(new Event(primaryID1())); assertThat(source.getSecondaryResource(primaryID1())).isNotPresent(); } @Test - public void noEventOnDeleteIfResourceWasNotInCacheBefore() { + void noEventOnDeleteIfResourceWasNotInCacheBefore() { source.handleDelete(primaryID1()); - verify(eventHandler, times(0)).handleEvent(eq(new Event(primaryID1()))); + verify(eventHandler, times(0)).handleEvent(new Event(primaryID1())); + } + + @Test + void handleMultipleResourceTrivialCase() { + source.handleResourcesUpdate(primaryID1(), Set.of(testResource1(), testResource2())); + + verify(eventHandler, times(1)).handleEvent(new Event(primaryID1())); + assertThat(source.getSecondaryResources(primaryID1())) + .containsExactlyInAnyOrder(testResource1(), testResource2()); + } + + @Test + void handleOneResourceRemovedFromMultiple() { + source.handleResourcesUpdate(primaryID1(), Set.of(testResource1(), testResource2())); + source.handleResourcesUpdate(primaryID1(), Set.of(testResource1())); + + verify(eventHandler, times(2)).handleEvent(new Event(primaryID1())); + assertThat(source.getSecondaryResources(primaryID1())).containsExactly(testResource1()); + } + + @Test + void addingAdditionalResource() { + source.handleResourcesUpdate(primaryID1(), Set.of(testResource1())); + source.handleResourcesUpdate(primaryID1(), Set.of(testResource1(), testResource2())); + + verify(eventHandler, times(2)).handleEvent(new Event(primaryID1())); + assertThat(source.getSecondaryResources(primaryID1())) + .containsExactlyInAnyOrder(testResource1(), testResource2()); } + @Test + void replacingResource() { + source.handleResourcesUpdate(primaryID1(), Set.of(testResource1())); + source.handleResourcesUpdate(primaryID1(), Set.of(testResource2())); + + verify(eventHandler, times(2)).handleEvent(new Event(primaryID1())); + assertThat(source.getSecondaryResources(primaryID1())).containsExactly(testResource2()); + } + + @Test + void handlesDeleteFromMultipleResources() { + source.handleResourcesUpdate(primaryID1(), Set.of(testResource1(), testResource2())); + source.handleDelete(primaryID1(), testResource1()); + + verify(eventHandler, times(2)).handleEvent(new Event(primaryID1())); + assertThat(source.getSecondaryResources(primaryID1())).containsExactly(testResource2()); + } + + @Test + void handlesDeleteAllFromMultipleResources() { + source.handleResourcesUpdate(primaryID1(), Set.of(testResource1(), testResource2())); + source.handleDeleteResources(primaryID1(), Set.of(testResource1(), testResource2())); + + verify(eventHandler, times(2)).handleEvent(new Event(primaryID1())); + assertThat(source.getSecondaryResources(primaryID1())).isEmpty(); + } public static class TestExternalCachingEventSource extends ExternalResourceCachingEventSource { From 8444c1def5d6f8d724ebd8fba149a9a67136bddf Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 22 Apr 2022 09:47:04 +0200 Subject: [PATCH 06/18] docs and other improvements --- .../external/PollingDependentResource.java | 8 +--- .../ExternalResourceCachingEventSource.java | 42 +++++++++++++------ .../processing/event/source/IDMapper.java | 5 ++- .../inbound/CachingInboundEventSource.java | 4 +- .../PerResourcePollingEventSource.java | 2 +- .../source/polling/PollingEventSource.java | 30 +++++++------ ...xternalResourceCachingEventSourceTest.java | 32 +++++++------- .../polling/PollingEventSourceTest.java | 12 +++--- 8 files changed, 77 insertions(+), 58 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java index a47bfe4070..59e6895e1d 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java @@ -1,18 +1,14 @@ package io.javaoperatorsdk.operator.processing.dependent.external; -import java.util.Map; -import java.util.Set; -import java.util.function.Supplier; - import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; -import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.source.IDMapper; import io.javaoperatorsdk.operator.processing.event.source.polling.PollingEventSource; public abstract class PollingDependentResource - extends AbstractPollingDependentResource implements Supplier>> { + extends AbstractPollingDependentResource + implements PollingEventSource.GenericResourceFetcher { private final IDMapper idProvider; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java index b5a3443d48..84145256dd 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java @@ -12,6 +12,24 @@ import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.ResourceID; +/** + * Handles caching and related operation of external event sources. It can handle multiple secondary + * resources for a single primary resources. + *

+ * There are two related concepts to understand: + *

    + *
  • IDMapper - maps or in other words extracts a id from the resources
  • + *
  • Object equals usage - compares if the two resources are the same or same version.
  • + *
+ * + * When a resource is added for a primary resource its key is used to put in a map. Equals is used + * to compare if it's still the same resource, or an updated version of it. Event is emitted only if + * a new resource(s) is received or actually updated or deleted. Delete is detected by a missing + * key. + * + * @param type of polled external secondary resource + * @param

primary resource + */ public abstract class ExternalResourceCachingEventSource extends AbstractResourceEventSource implements RecentOperationCacheFiller { @@ -33,11 +51,7 @@ protected synchronized void handleDelete(ResourceID primaryID) { } } - protected synchronized void handleDelete(ResourceID primaryID, String resourceID) { - handleDelete(primaryID, Set.of(resourceID)); - } - - protected synchronized void handleDeleteResources(ResourceID primaryID, Set resource) { + protected synchronized void handleDeletes(ResourceID primaryID, Set resource) { handleDelete(primaryID, resource.stream().map(idMapper).collect(Collectors.toSet())); } @@ -61,22 +75,22 @@ protected synchronized void handleDelete(ResourceID primaryID, Set resou } } - protected synchronized void handleResourcesUpdate(ResourceID primaryID, R actualResource) { - handleResourcesUpdate(primaryID, Set.of(actualResource), true); + protected synchronized void handleResources(ResourceID primaryID, R actualResource) { + handleResources(primaryID, Set.of(actualResource), true); } - protected synchronized void handleResourcesUpdate(ResourceID primaryID, Set newResources) { - handleResourcesUpdate(primaryID, newResources, true); + protected synchronized void handleResources(ResourceID primaryID, Set newResources) { + handleResources(primaryID, newResources, true); } - protected synchronized void handleResourcesUpdate(Map> allNewResources) { + protected synchronized void handleResources(Map> allNewResources) { var toDelete = cache.keySet().stream().filter(k -> !allNewResources.containsKey(k)) .collect(Collectors.toList()); toDelete.forEach(this::handleDelete); - allNewResources.forEach((primaryID, resources) -> handleResourcesUpdate(primaryID, resources)); + allNewResources.forEach((primaryID, resources) -> handleResources(primaryID, resources)); } - protected synchronized void handleResourcesUpdate(ResourceID primaryID, Set newResources, + protected synchronized void handleResources(ResourceID primaryID, Set newResources, boolean propagateEvent) { log.debug("Handling resources update for: {} numberOfResources: {} ", primaryID, newResources.size()); @@ -141,4 +155,8 @@ public Optional getSecondaryResource(ResourceID primaryID) { throw new IllegalStateException("More than 1 secondary resource related to primary"); } } + + public Map> getCache() { + return Collections.unmodifiableMap(cache); + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java index 21abea8a62..f0ad09a121 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java @@ -5,9 +5,10 @@ public interface IDMapper extends Function { /** - * If a polling event source handles only single secondary resources + * Used If a polling event source handles only single secondary resource. See also docs for: + * {@link ExternalResourceCachingEventSource} * - * @return static id mapper, all resources are mapped for same id + * @return static id mapper, all resources are mapped for same id. * @param */ static IDMapper singleResourceIDMapper() { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java index f2f08baeb4..d3aa97f2ef 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java @@ -15,11 +15,11 @@ public CachingInboundEventSource(Class resourceClass, IDMapper idProvider) } public void handleResourceEvent(ResourceID primaryID, Set resources) { - super.handleResourcesUpdate(primaryID, resources); + super.handleResources(primaryID, resources); } public void handleResourceEvent(ResourceID primaryID, R resource) { - super.handleResourcesUpdate(primaryID, resource); + super.handleResources(primaryID, resource); } public void handleResourceDeleteEvent(ResourceID primaryID, String resourceID) { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java index cd837fcde4..c0427b2d73 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java @@ -65,7 +65,7 @@ public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, private Set getAndCacheResource(P primary, boolean fromGetter) { var values = resourceFetcher.fetchResources(primary); - handleResourcesUpdate(ResourceID.fromResource(primary), values, !fromGetter); + handleResources(ResourceID.fromResource(primary), values, !fromGetter); fetchedForPrimaries.add(ResourceID.fromResource(primary)); return values; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java index ab54234c2c..7229687178 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java @@ -4,7 +4,6 @@ import java.util.Set; import java.util.Timer; import java.util.TimerTask; -import java.util.function.Supplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,11 +16,11 @@ /** * Polls resource (on contrary to {@link PerResourcePollingEventSource}) not per resource bases but - * instead to calls supplier periodically and independently of the number of state of custom - * resources managed by the operator. It is called on start (synced). This means that when the - * reconciler first time executed on startup a poll already happened before. So if the cache does - * not contain the target resource it means it is not created yet or was deleted while an operator - * was not running. + * instead to calls supplier periodically and independently of the number or state of custom + * resources managed by the controller. It is called on start (synced). This means that when the + * reconciler first time executed on startup the first poll already happened before. So if the cache + * does not contain the target resource it means it is not created yet or was deleted while an + * operator was not running. * *

* Another caveat with this is if the cached object is checked in the reconciler and created since @@ -47,25 +46,25 @@ public class PollingEventSource private static final Logger log = LoggerFactory.getLogger(PollingEventSource.class); private final Timer timer = new Timer(); - private final Supplier>> supplierToPoll; + private final GenericResourceFetcher genericResourceFetcher; private final long period; public PollingEventSource( - Supplier>> supplier, + GenericResourceFetcher supplier, long period, Class resourceClass) { super(resourceClass, IDMapper.singleResourceIDMapper()); - this.supplierToPoll = supplier; + this.genericResourceFetcher = supplier; this.period = period; } public PollingEventSource( - Supplier>> supplier, + GenericResourceFetcher supplier, long period, Class resourceClass, IDMapper idMapper) { super(resourceClass, idMapper); - this.supplierToPoll = supplier; + this.genericResourceFetcher = supplier; this.period = period; } @@ -89,8 +88,13 @@ public void run() { } protected synchronized void getStateAndFillCache() { - var values = supplierToPoll.get(); - handleResourcesUpdate(values); + var values = genericResourceFetcher.fetchResources(); + handleResources(values); + } + + + public interface GenericResourceFetcher { + Map> fetchResources(); } @Override diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java index 6f330f8948..72a578421c 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java @@ -23,7 +23,7 @@ public void setup() { @Test void putsNewResourceIntoCacheAndProducesEvent() { - source.handleResourcesUpdate(primaryID1(), testResource1()); + source.handleResources(primaryID1(), testResource1()); verify(eventHandler, times(1)).handleEvent(new Event(primaryID1())); assertThat(source.getSecondaryResource(primaryID1())).isPresent(); @@ -33,8 +33,8 @@ void putsNewResourceIntoCacheAndProducesEvent() { void propagatesEventIfResourceChanged() { var res2 = testResource1(); res2.setValue("changedValue"); - source.handleResourcesUpdate(primaryID1(), testResource1()); - source.handleResourcesUpdate(primaryID1(), res2); + source.handleResources(primaryID1(), testResource1()); + source.handleResources(primaryID1(), res2); verify(eventHandler, times(2)).handleEvent(new Event(primaryID1())); assertThat(source.getSecondaryResource(primaryID1())).contains(res2); @@ -42,8 +42,8 @@ void propagatesEventIfResourceChanged() { @Test void noEventPropagatedIfTheResourceIsNotChanged() { - source.handleResourcesUpdate(primaryID1(), testResource1()); - source.handleResourcesUpdate(primaryID1(), testResource1()); + source.handleResources(primaryID1(), testResource1()); + source.handleResources(primaryID1(), testResource1()); verify(eventHandler, times(1)).handleEvent(new Event(primaryID1())); assertThat(source.getSecondaryResource(primaryID1())).isPresent(); @@ -51,7 +51,7 @@ void noEventPropagatedIfTheResourceIsNotChanged() { @Test void propagatesEventOnDeleteIfThereIsPrevResourceInCache() { - source.handleResourcesUpdate(primaryID1(), testResource1()); + source.handleResources(primaryID1(), testResource1()); source.handleDelete(primaryID1()); verify(eventHandler, times(2)).handleEvent(new Event(primaryID1())); @@ -67,7 +67,7 @@ void noEventOnDeleteIfResourceWasNotInCacheBefore() { @Test void handleMultipleResourceTrivialCase() { - source.handleResourcesUpdate(primaryID1(), Set.of(testResource1(), testResource2())); + source.handleResources(primaryID1(), Set.of(testResource1(), testResource2())); verify(eventHandler, times(1)).handleEvent(new Event(primaryID1())); assertThat(source.getSecondaryResources(primaryID1())) @@ -76,8 +76,8 @@ void handleMultipleResourceTrivialCase() { @Test void handleOneResourceRemovedFromMultiple() { - source.handleResourcesUpdate(primaryID1(), Set.of(testResource1(), testResource2())); - source.handleResourcesUpdate(primaryID1(), Set.of(testResource1())); + source.handleResources(primaryID1(), Set.of(testResource1(), testResource2())); + source.handleResources(primaryID1(), Set.of(testResource1())); verify(eventHandler, times(2)).handleEvent(new Event(primaryID1())); assertThat(source.getSecondaryResources(primaryID1())).containsExactly(testResource1()); @@ -85,8 +85,8 @@ void handleOneResourceRemovedFromMultiple() { @Test void addingAdditionalResource() { - source.handleResourcesUpdate(primaryID1(), Set.of(testResource1())); - source.handleResourcesUpdate(primaryID1(), Set.of(testResource1(), testResource2())); + source.handleResources(primaryID1(), Set.of(testResource1())); + source.handleResources(primaryID1(), Set.of(testResource1(), testResource2())); verify(eventHandler, times(2)).handleEvent(new Event(primaryID1())); assertThat(source.getSecondaryResources(primaryID1())) @@ -95,8 +95,8 @@ void addingAdditionalResource() { @Test void replacingResource() { - source.handleResourcesUpdate(primaryID1(), Set.of(testResource1())); - source.handleResourcesUpdate(primaryID1(), Set.of(testResource2())); + source.handleResources(primaryID1(), Set.of(testResource1())); + source.handleResources(primaryID1(), Set.of(testResource2())); verify(eventHandler, times(2)).handleEvent(new Event(primaryID1())); assertThat(source.getSecondaryResources(primaryID1())).containsExactly(testResource2()); @@ -104,7 +104,7 @@ void replacingResource() { @Test void handlesDeleteFromMultipleResources() { - source.handleResourcesUpdate(primaryID1(), Set.of(testResource1(), testResource2())); + source.handleResources(primaryID1(), Set.of(testResource1(), testResource2())); source.handleDelete(primaryID1(), testResource1()); verify(eventHandler, times(2)).handleEvent(new Event(primaryID1())); @@ -113,8 +113,8 @@ void handlesDeleteFromMultipleResources() { @Test void handlesDeleteAllFromMultipleResources() { - source.handleResourcesUpdate(primaryID1(), Set.of(testResource1(), testResource2())); - source.handleDeleteResources(primaryID1(), Set.of(testResource1(), testResource2())); + source.handleResources(primaryID1(), Set.of(testResource1(), testResource2())); + source.handleDeletes(primaryID1(), Set.of(testResource1(), testResource2())); verify(eventHandler, times(2)).handleEvent(new Event(primaryID1())); assertThat(source.getSecondaryResources(primaryID1())).isEmpty(); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java index 03def1c913..bbfa18a4ed 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java @@ -3,7 +3,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -import java.util.function.Supplier; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -21,9 +20,10 @@ class PollingEventSourceTest extends AbstractEventSourceTestBase, EventHandler> { - private Supplier>> supplier = mock(Supplier.class); + private PollingEventSource.GenericResourceFetcher resourceFetcher = + mock(PollingEventSource.GenericResourceFetcher.class); private final PollingEventSource pollingEventSource = - new PollingEventSource<>(supplier, 50L, SampleExternalResource.class, + new PollingEventSource<>(resourceFetcher, 50L, SampleExternalResource.class, (SampleExternalResource er) -> er.getName() + "#" + er.getValue()); @BeforeEach @@ -33,7 +33,7 @@ public void setup() { @Test void pollsAndProcessesEvents() throws InterruptedException { - when(supplier.get()).thenReturn(testResponseWithTwoValues()); + when(resourceFetcher.fetchResources()).thenReturn(testResponseWithTwoValues()); pollingEventSource.start(); Thread.sleep(100); @@ -42,7 +42,7 @@ void pollsAndProcessesEvents() throws InterruptedException { @Test void propagatesEventForRemovedResources() throws InterruptedException { - when(supplier.get()).thenReturn(testResponseWithTwoValues()) + when(resourceFetcher.fetchResources()).thenReturn(testResponseWithTwoValues()) .thenReturn(testResponseWithOneValue()); pollingEventSource.start(); Thread.sleep(150); @@ -52,7 +52,7 @@ void propagatesEventForRemovedResources() throws InterruptedException { @Test void doesNotPropagateEventIfResourceNotChanged() throws InterruptedException { - when(supplier.get()).thenReturn(testResponseWithTwoValues()); + when(resourceFetcher.fetchResources()).thenReturn(testResponseWithTwoValues()); pollingEventSource.start(); Thread.sleep(250); From 44987c93543814e7c3b8d5f1246adb7a8ab47f5e Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 22 Apr 2022 09:47:44 +0200 Subject: [PATCH 07/18] docs --- .../operator/processing/event/source/IDMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java index f0ad09a121..797523694a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java @@ -9,7 +9,7 @@ public interface IDMapper extends Function { * {@link ExternalResourceCachingEventSource} * * @return static id mapper, all resources are mapped for same id. - * @param + * @param secondary resource type */ static IDMapper singleResourceIDMapper() { return r -> "id"; From f8f7c5bae8ab4df12a258d10eacd01300cfe5bdb Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Fri, 22 Apr 2022 15:17:07 +0200 Subject: [PATCH 08/18] fix: typo --- .../operator/processing/event/source/IDMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java index 797523694a..64d0cdc4ff 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java @@ -5,7 +5,7 @@ public interface IDMapper extends Function { /** - * Used If a polling event source handles only single secondary resource. See also docs for: + * Used if a polling event source handles only single secondary resource. See also docs for: * {@link ExternalResourceCachingEventSource} * * @return static id mapper, all resources are mapped for same id. From 7325a941b770d2a14b582c86a53c942af2b15d07 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Fri, 22 Apr 2022 15:17:25 +0200 Subject: [PATCH 09/18] refactor: simplify --- .../processing/MultiResourceOwner.java | 23 ------------------- .../event/source/ResourceEventSource.java | 20 ++++++++++++++-- 2 files changed, 18 insertions(+), 25 deletions(-) delete mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MultiResourceOwner.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MultiResourceOwner.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MultiResourceOwner.java deleted file mode 100644 index 12d19f84c1..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MultiResourceOwner.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.javaoperatorsdk.operator.processing; - -import java.util.Optional; -import java.util.Set; - -import io.fabric8.kubernetes.api.model.HasMetadata; - -public interface MultiResourceOwner extends ResourceOwner { - - default Optional getSecondaryResource(P primary) { - var resources = getSecondaryResources(primary); - if (resources.isEmpty()) { - return Optional.empty(); - } else if (resources.size() == 1) { - return Optional.of(resources.iterator().next()); - } else { - throw new IllegalStateException("More than 1 secondary resource related to primary"); - } - - } - - Set getSecondaryResources(P primary); -} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java index e0f031c044..d57a662d82 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventSource.java @@ -1,9 +1,25 @@ package io.javaoperatorsdk.operator.processing.event.source; +import java.util.Optional; +import java.util.Set; + import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.MultiResourceOwner; +import io.javaoperatorsdk.operator.processing.ResourceOwner; public interface ResourceEventSource extends EventSource, - MultiResourceOwner { + ResourceOwner { + + default Optional getSecondaryResource(P primary) { + var resources = getSecondaryResources(primary); + if (resources.isEmpty()) { + return Optional.empty(); + } else if (resources.size() == 1) { + return Optional.of(resources.iterator().next()); + } else { + throw new IllegalStateException("More than 1 secondary resource related to primary"); + } + + } + Set getSecondaryResources(P primary); } From 0d61e550146ebd6a110846411ee2083705522dd3 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 22 Apr 2022 15:50:01 +0200 Subject: [PATCH 10/18] fix: id mapper --- .../external/AbstractPollingDependentResource.java | 4 ++-- .../source/ExternalResourceCachingEventSource.java | 10 +++++----- .../operator/processing/event/source/IDMapper.java | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java index fcde777b23..056b22e59d 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java @@ -28,7 +28,7 @@ public long getPollingPeriod() { // for now dependent resources support event sources only with one owned resource. @Override - public String apply(R r) { - return IDMapper.singleResourceIDMapper().apply(r); + public String getID(R r) { + return IDMapper.singleResourceIDMapper().getID(r); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java index 84145256dd..8f4f19cc75 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java @@ -52,11 +52,11 @@ protected synchronized void handleDelete(ResourceID primaryID) { } protected synchronized void handleDeletes(ResourceID primaryID, Set resource) { - handleDelete(primaryID, resource.stream().map(idMapper).collect(Collectors.toSet())); + handleDelete(primaryID, resource.stream().map(idMapper::getID).collect(Collectors.toSet())); } protected synchronized void handleDelete(ResourceID primaryID, R resource) { - handleDelete(primaryID, Set.of(idMapper.apply(resource))); + handleDelete(primaryID, Set.of(idMapper.getID(resource))); } protected synchronized void handleDelete(ResourceID primaryID, Set resourceID) { @@ -98,7 +98,7 @@ protected synchronized void handleResources(ResourceID primaryID, Set newReso return; } var cachedResources = cache.get(primaryID); - var newResourcesMap = newResources.stream().collect(Collectors.toMap(idMapper, r -> r)); + var newResourcesMap = newResources.stream().collect(Collectors.toMap(idMapper::getID, r -> r)); cache.put(primaryID, newResourcesMap); if (propagateEvent && !newResourcesMap.equals(cachedResources)) { getEventHandler().handleEvent(new Event(primaryID)); @@ -108,7 +108,7 @@ protected synchronized void handleResources(ResourceID primaryID, Set newReso @Override public synchronized void handleRecentResourceCreate(ResourceID primaryID, R resource) { var actualValues = cache.get(primaryID); - var resourceId = idMapper.apply(resource); + var resourceId = idMapper.getID(resource); if (actualValues == null) { actualValues = new HashMap<>(); cache.put(primaryID, actualValues); @@ -123,7 +123,7 @@ public synchronized void handleRecentResourceUpdate( ResourceID primaryID, R resource, R previousVersionOfResource) { var actualValues = cache.get(primaryID); if (actualValues != null) { - var resourceId = idMapper.apply(resource); + var resourceId = idMapper.getID(resource); R actualResource = actualValues.get(resourceId); if (actualResource.equals(previousVersionOfResource)) { actualValues.put(resourceId, resource); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java index 797523694a..41cad5d38f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java @@ -1,8 +1,8 @@ package io.javaoperatorsdk.operator.processing.event.source; -import java.util.function.Function; +public interface IDMapper { -public interface IDMapper extends Function { + String getID(R var1); /** * Used If a polling event source handles only single secondary resource. See also docs for: From 90ba3a6a60bb41c79d51fd65c58807a761e7386e Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 22 Apr 2022 16:48:20 +0200 Subject: [PATCH 11/18] naming improvements --- .../AbstractPollingDependentResource.java | 8 ++++---- .../external/PollingDependentResource.java | 8 ++++---- .../{IDMapper.java => CacheKeyMapper.java} | 6 +++--- .../ExternalResourceCachingEventSource.java | 19 +++++++++++-------- .../inbound/CachingInboundEventSource.java | 4 ++-- .../PerResourcePollingEventSource.java | 8 ++++---- .../source/polling/PollingEventSource.java | 8 ++++---- .../PerResourcePollingEventSourceTest.java | 4 ++-- 8 files changed, 34 insertions(+), 31 deletions(-) rename operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/{IDMapper.java => CacheKeyMapper.java} (75%) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java index 056b22e59d..029841ad8a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java @@ -1,10 +1,10 @@ package io.javaoperatorsdk.operator.processing.dependent.external; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.event.source.IDMapper; +import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; public abstract class AbstractPollingDependentResource - extends AbstractCachingDependentResource implements IDMapper { + extends AbstractCachingDependentResource implements CacheKeyMapper { public static final int DEFAULT_POLLING_PERIOD = 5000; private long pollingPeriod; @@ -28,7 +28,7 @@ public long getPollingPeriod() { // for now dependent resources support event sources only with one owned resource. @Override - public String getID(R r) { - return IDMapper.singleResourceIDMapper().getID(r); + public String keyFor(R r) { + return CacheKeyMapper.singleResourceIDMapper().keyFor(r); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java index 59e6895e1d..e9a9bdc696 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java @@ -2,23 +2,23 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; +import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; -import io.javaoperatorsdk.operator.processing.event.source.IDMapper; import io.javaoperatorsdk.operator.processing.event.source.polling.PollingEventSource; public abstract class PollingDependentResource extends AbstractPollingDependentResource implements PollingEventSource.GenericResourceFetcher { - private final IDMapper idProvider; + private final CacheKeyMapper idProvider; - public PollingDependentResource(Class resourceType, IDMapper idProvider) { + public PollingDependentResource(Class resourceType, CacheKeyMapper idProvider) { super(resourceType); this.idProvider = idProvider; } public PollingDependentResource(Class resourceType, long pollingPeriod, - IDMapper idProvider) { + CacheKeyMapper idProvider) { super(resourceType, pollingPeriod); this.idProvider = idProvider; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java similarity index 75% rename from operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java rename to operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java index da555685c7..0494cb3414 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/IDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java @@ -1,8 +1,8 @@ package io.javaoperatorsdk.operator.processing.event.source; -public interface IDMapper { +public interface CacheKeyMapper { - String getID(R var1); + String keyFor(R var1); /** * Used if a polling event source handles only single secondary resource. See also docs for: @@ -11,7 +11,7 @@ public interface IDMapper { * @return static id mapper, all resources are mapped for same id. * @param secondary resource type */ - static IDMapper singleResourceIDMapper() { + static CacheKeyMapper singleResourceIDMapper() { return r -> "id"; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java index 8f4f19cc75..e35f792f4e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java @@ -35,13 +35,14 @@ public abstract class ExternalResourceCachingEventSource idMapper; + protected final CacheKeyMapper cacheKeyMapper; protected Map> cache = new ConcurrentHashMap<>(); - protected ExternalResourceCachingEventSource(Class resourceClass, IDMapper idMapper) { + protected ExternalResourceCachingEventSource(Class resourceClass, + CacheKeyMapper cacheKeyMapper) { super(resourceClass); - this.idMapper = idMapper; + this.cacheKeyMapper = cacheKeyMapper; } protected synchronized void handleDelete(ResourceID primaryID) { @@ -52,11 +53,12 @@ protected synchronized void handleDelete(ResourceID primaryID) { } protected synchronized void handleDeletes(ResourceID primaryID, Set resource) { - handleDelete(primaryID, resource.stream().map(idMapper::getID).collect(Collectors.toSet())); + handleDelete(primaryID, + resource.stream().map(cacheKeyMapper::keyFor).collect(Collectors.toSet())); } protected synchronized void handleDelete(ResourceID primaryID, R resource) { - handleDelete(primaryID, Set.of(idMapper.getID(resource))); + handleDelete(primaryID, Set.of(cacheKeyMapper.keyFor(resource))); } protected synchronized void handleDelete(ResourceID primaryID, Set resourceID) { @@ -98,7 +100,8 @@ protected synchronized void handleResources(ResourceID primaryID, Set newReso return; } var cachedResources = cache.get(primaryID); - var newResourcesMap = newResources.stream().collect(Collectors.toMap(idMapper::getID, r -> r)); + var newResourcesMap = + newResources.stream().collect(Collectors.toMap(cacheKeyMapper::keyFor, r -> r)); cache.put(primaryID, newResourcesMap); if (propagateEvent && !newResourcesMap.equals(cachedResources)) { getEventHandler().handleEvent(new Event(primaryID)); @@ -108,7 +111,7 @@ protected synchronized void handleResources(ResourceID primaryID, Set newReso @Override public synchronized void handleRecentResourceCreate(ResourceID primaryID, R resource) { var actualValues = cache.get(primaryID); - var resourceId = idMapper.getID(resource); + var resourceId = cacheKeyMapper.keyFor(resource); if (actualValues == null) { actualValues = new HashMap<>(); cache.put(primaryID, actualValues); @@ -123,7 +126,7 @@ public synchronized void handleRecentResourceUpdate( ResourceID primaryID, R resource, R previousVersionOfResource) { var actualValues = cache.get(primaryID); if (actualValues != null) { - var resourceId = idMapper.getID(resource); + var resourceId = cacheKeyMapper.keyFor(resource); R actualResource = actualValues.get(resourceId); if (actualResource.equals(previousVersionOfResource)) { actualValues.put(resourceId, resource); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java index d3aa97f2ef..2df809418c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java @@ -4,13 +4,13 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; -import io.javaoperatorsdk.operator.processing.event.source.IDMapper; public class CachingInboundEventSource extends ExternalResourceCachingEventSource { - public CachingInboundEventSource(Class resourceClass, IDMapper idProvider) { + public CachingInboundEventSource(Class resourceClass, CacheKeyMapper idProvider) { super(resourceClass, idProvider); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java index c0427b2d73..7253807359 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java @@ -11,9 +11,9 @@ import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.Cache; +import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; import io.javaoperatorsdk.operator.processing.event.source.CachingEventSource; import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; -import io.javaoperatorsdk.operator.processing.event.source.IDMapper; import io.javaoperatorsdk.operator.processing.event.source.ResourceEventAware; /** @@ -44,18 +44,18 @@ public class PerResourcePollingEventSource public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, Cache

resourceCache, long period, Class resourceClass) { this(resourceFetcher, resourceCache, period, null, resourceClass, - IDMapper.singleResourceIDMapper()); + CacheKeyMapper.singleResourceIDMapper()); } public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, - Cache

resourceCache, long period, Class resourceClass, IDMapper idProvider) { + Cache

resourceCache, long period, Class resourceClass, CacheKeyMapper idProvider) { this(resourceFetcher, resourceCache, period, null, resourceClass, idProvider); } public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, Cache

resourceCache, long period, Predicate

registerPredicate, Class resourceClass, - IDMapper idProvider) { + CacheKeyMapper idProvider) { super(resourceClass, idProvider); this.resourceFetcher = resourceFetcher; this.resourceCache = resourceCache; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java index 7229687178..60ffa799ef 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java @@ -11,8 +11,8 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; -import io.javaoperatorsdk.operator.processing.event.source.IDMapper; /** * Polls resource (on contrary to {@link PerResourcePollingEventSource}) not per resource bases but @@ -53,7 +53,7 @@ public PollingEventSource( GenericResourceFetcher supplier, long period, Class resourceClass) { - super(resourceClass, IDMapper.singleResourceIDMapper()); + super(resourceClass, CacheKeyMapper.singleResourceIDMapper()); this.genericResourceFetcher = supplier; this.period = period; } @@ -62,8 +62,8 @@ public PollingEventSource( GenericResourceFetcher supplier, long period, Class resourceClass, - IDMapper idMapper) { - super(resourceClass, idMapper); + CacheKeyMapper cacheKeyMapper) { + super(resourceClass, cacheKeyMapper); this.genericResourceFetcher = supplier; this.period = period; } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java index 58ef00b3a4..1320285b41 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java @@ -11,7 +11,7 @@ import io.javaoperatorsdk.operator.processing.event.EventHandler; import io.javaoperatorsdk.operator.processing.event.source.AbstractEventSourceTestBase; import io.javaoperatorsdk.operator.processing.event.source.Cache; -import io.javaoperatorsdk.operator.processing.event.source.IDMapper; +import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; import io.javaoperatorsdk.operator.processing.event.source.SampleExternalResource; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; @@ -57,7 +57,7 @@ void pollsTheResourceAfterAwareOfIt() throws InterruptedException { void registeringTaskOnAPredicate() throws InterruptedException { setUpSource(new PerResourcePollingEventSource<>(supplier, resourceCache, PERIOD, testCustomResource -> testCustomResource.getMetadata().getGeneration() > 1, - SampleExternalResource.class, IDMapper.singleResourceIDMapper())); + SampleExternalResource.class, CacheKeyMapper.singleResourceIDMapper())); source.onResourceCreated(testCustomResource); Thread.sleep(2 * PERIOD); From fec65d8199eb5433a28eb4e3cb9205ba022f76a3 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 22 Apr 2022 16:55:27 +0200 Subject: [PATCH 12/18] naming fix --- .../external/AbstractPollingDependentResource.java | 2 +- .../dependent/external/PollingDependentResource.java | 12 ++++++------ .../processing/event/source/CacheKeyMapper.java | 2 +- .../polling/PerResourcePollingEventSource.java | 2 +- .../event/source/polling/PollingEventSource.java | 2 +- .../polling/PerResourcePollingEventSourceTest.java | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java index 029841ad8a..ea25e3a5b5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java @@ -29,6 +29,6 @@ public long getPollingPeriod() { // for now dependent resources support event sources only with one owned resource. @Override public String keyFor(R r) { - return CacheKeyMapper.singleResourceIDMapper().keyFor(r); + return CacheKeyMapper.singleResourceCacheKeyMapper().keyFor(r); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java index e9a9bdc696..9fb293b1f5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java @@ -10,23 +10,23 @@ public abstract class PollingDependentResource extends AbstractPollingDependentResource implements PollingEventSource.GenericResourceFetcher { - private final CacheKeyMapper idProvider; + private final CacheKeyMapper cacheKeyMapper; - public PollingDependentResource(Class resourceType, CacheKeyMapper idProvider) { + public PollingDependentResource(Class resourceType, CacheKeyMapper cacheKeyMapper) { super(resourceType); - this.idProvider = idProvider; + this.cacheKeyMapper = cacheKeyMapper; } public PollingDependentResource(Class resourceType, long pollingPeriod, - CacheKeyMapper idProvider) { + CacheKeyMapper cacheKeyMapper) { super(resourceType, pollingPeriod); - this.idProvider = idProvider; + this.cacheKeyMapper = cacheKeyMapper; } @Override protected ExternalResourceCachingEventSource createEventSource( EventSourceContext

context) { - return new PollingEventSource<>(this, getPollingPeriod(), resourceType(), idProvider); + return new PollingEventSource<>(this, getPollingPeriod(), resourceType(), cacheKeyMapper); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java index 0494cb3414..5c98fb7f4b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java @@ -11,7 +11,7 @@ public interface CacheKeyMapper { * @return static id mapper, all resources are mapped for same id. * @param secondary resource type */ - static CacheKeyMapper singleResourceIDMapper() { + static CacheKeyMapper singleResourceCacheKeyMapper() { return r -> "id"; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java index 7253807359..05cc63835a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java @@ -44,7 +44,7 @@ public class PerResourcePollingEventSource public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, Cache

resourceCache, long period, Class resourceClass) { this(resourceFetcher, resourceCache, period, null, resourceClass, - CacheKeyMapper.singleResourceIDMapper()); + CacheKeyMapper.singleResourceCacheKeyMapper()); } public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java index 60ffa799ef..09ff2e8b0e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java @@ -53,7 +53,7 @@ public PollingEventSource( GenericResourceFetcher supplier, long period, Class resourceClass) { - super(resourceClass, CacheKeyMapper.singleResourceIDMapper()); + super(resourceClass, CacheKeyMapper.singleResourceCacheKeyMapper()); this.genericResourceFetcher = supplier; this.period = period; } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java index 1320285b41..a5d6210c1b 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java @@ -57,7 +57,7 @@ void pollsTheResourceAfterAwareOfIt() throws InterruptedException { void registeringTaskOnAPredicate() throws InterruptedException { setUpSource(new PerResourcePollingEventSource<>(supplier, resourceCache, PERIOD, testCustomResource -> testCustomResource.getMetadata().getGeneration() > 1, - SampleExternalResource.class, CacheKeyMapper.singleResourceIDMapper())); + SampleExternalResource.class, CacheKeyMapper.singleResourceCacheKeyMapper())); source.onResourceCreated(testCustomResource); Thread.sleep(2 * PERIOD); From 49504b758abf4a46a078aa3e36e44fae3468a895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 22 Apr 2022 16:57:23 +0200 Subject: [PATCH 13/18] Update operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java Co-authored-by: Chris Laprun --- .../event/source/ExternalResourceCachingEventSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java index e35f792f4e..66cb9426b4 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java @@ -18,7 +18,7 @@ *

* There are two related concepts to understand: *

    - *
  • IDMapper - maps or in other words extracts a id from the resources
  • + *
  • CacheKeyMapper - maps/extracts a key used to reference the associated resource in the cache
  • *
  • Object equals usage - compares if the two resources are the same or same version.
  • *
* From a4054b83fd345365dd4f9c5ac370fedd6f0de490 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 22 Apr 2022 17:00:00 +0200 Subject: [PATCH 14/18] fix: format --- .../event/source/ExternalResourceCachingEventSource.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java index 66cb9426b4..f8a0cafcd8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java @@ -18,7 +18,8 @@ *

* There are two related concepts to understand: *

    - *
  • CacheKeyMapper - maps/extracts a key used to reference the associated resource in the cache
  • + *
  • CacheKeyMapper - maps/extracts a key used to reference the associated resource in the + * cache
  • *
  • Object equals usage - compares if the two resources are the same or same version.
  • *
* From 8b48c1d13f2efac11f965d2fc8c9f9baaa8631ec Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 22 Apr 2022 17:00:00 +0200 Subject: [PATCH 15/18] fix: format --- .../ExternalResourceCachingEventSource.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java index 66cb9426b4..f0423b8edc 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java @@ -18,14 +18,16 @@ *

* There are two related concepts to understand: *

    - *
  • CacheKeyMapper - maps/extracts a key used to reference the associated resource in the cache
  • - *
  • Object equals usage - compares if the two resources are the same or same version.
  • + *
  • {@link CacheKeyMapper} - maps/extracts a key used to reference the associated resource in the + * cache
  • + *
  • External resources must properly implement {@link Object#equals(Object)} as + * the cache uses {@code equals} to check if the resource has changed
  • *
- * - * When a resource is added for a primary resource its key is used to put in a map. Equals is used - * to compare if it's still the same resource, or an updated version of it. Event is emitted only if - * a new resource(s) is received or actually updated or deleted. Delete is detected by a missing - * key. + *

+ * When a resource is added for a primary resource its key is used to put in a map. + * {@link Object#equals(Object)} is used to compare if it's still the same resource, or an updated + * version of it. Event is emitted only if a new resource(s) is received or actually updated or + * deleted. Delete is detected by a missing key. * * @param type of polled external secondary resource * @param

primary resource @@ -33,7 +35,8 @@ public abstract class ExternalResourceCachingEventSource extends AbstractResourceEventSource implements RecentOperationCacheFiller { - private static Logger log = LoggerFactory.getLogger(ExternalResourceCachingEventSource.class); + private static final Logger log = + LoggerFactory.getLogger(ExternalResourceCachingEventSource.class); protected final CacheKeyMapper cacheKeyMapper; @@ -89,7 +92,7 @@ protected synchronized void handleResources(Map> allNewResour var toDelete = cache.keySet().stream().filter(k -> !allNewResources.containsKey(k)) .collect(Collectors.toList()); toDelete.forEach(this::handleDelete); - allNewResources.forEach((primaryID, resources) -> handleResources(primaryID, resources)); + allNewResources.forEach(this::handleResources); } protected synchronized void handleResources(ResourceID primaryID, Set newResources, From b6f6b5c62a2d4fd45947513e1115cbc407ba7477 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Fri, 22 Apr 2022 17:21:19 +0200 Subject: [PATCH 16/18] refactor: rename parameter more explicitly --- .../external/AbstractPollingDependentResource.java | 4 ++-- .../operator/processing/event/source/CacheKeyMapper.java | 2 +- .../event/source/inbound/CachingInboundEventSource.java | 4 ++-- .../source/polling/PerResourcePollingEventSource.java | 8 ++++---- .../api/reconciler/EventSourceInitializerTest.java | 5 +++-- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java index ea25e3a5b5..f3812a095f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java @@ -28,7 +28,7 @@ public long getPollingPeriod() { // for now dependent resources support event sources only with one owned resource. @Override - public String keyFor(R r) { - return CacheKeyMapper.singleResourceCacheKeyMapper().keyFor(r); + public String keyFor(R resource) { + return CacheKeyMapper.singleResourceCacheKeyMapper().keyFor(resource); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java index 5c98fb7f4b..d290e15496 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java @@ -2,7 +2,7 @@ public interface CacheKeyMapper { - String keyFor(R var1); + String keyFor(R resource); /** * Used if a polling event source handles only single secondary resource. See also docs for: diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java index 2df809418c..6d421a6460 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java @@ -10,8 +10,8 @@ public class CachingInboundEventSource extends ExternalResourceCachingEventSource { - public CachingInboundEventSource(Class resourceClass, CacheKeyMapper idProvider) { - super(resourceClass, idProvider); + public CachingInboundEventSource(Class resourceClass, CacheKeyMapper cacheKeyMapper) { + super(resourceClass, cacheKeyMapper); } public void handleResourceEvent(ResourceID primaryID, Set resources) { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java index 05cc63835a..05dfba033c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java @@ -48,15 +48,15 @@ public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, } public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, - Cache

resourceCache, long period, Class resourceClass, CacheKeyMapper idProvider) { - this(resourceFetcher, resourceCache, period, null, resourceClass, idProvider); + Cache

resourceCache, long period, Class resourceClass, CacheKeyMapper cacheKeyMapper) { + this(resourceFetcher, resourceCache, period, null, resourceClass, cacheKeyMapper); } public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, Cache

resourceCache, long period, Predicate

registerPredicate, Class resourceClass, - CacheKeyMapper idProvider) { - super(resourceClass, idProvider); + CacheKeyMapper cacheKeyMapper) { + super(resourceClass, cacheKeyMapper); this.resourceFetcher = resourceFetcher; this.resourceCache = resourceCache; this.period = period; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializerTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializerTest.java index f44170b588..b89ae730ee 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializerTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceInitializerTest.java @@ -11,9 +11,10 @@ class EventSourceInitializerTest { @Test + @SuppressWarnings({"rawtypes", "unchecked"}) void defaultNameDifferentForOtherInstance() { - var eventSource1 = new PollingEventSource(() -> new HashMap<>(), 1000, String.class); - var eventSource2 = new PollingEventSource(() -> new HashMap<>(), 1000, String.class); + var eventSource1 = new PollingEventSource(HashMap::new, 1000, String.class); + var eventSource2 = new PollingEventSource(HashMap::new, 1000, String.class); var eventSourceName1 = EventSourceInitializer.generateNameFor(eventSource1); var eventSourceName2 = EventSourceInitializer.generateNameFor(eventSource2); From 1ed4cb483b736e22603e4aa1ae8a22855114204b Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Fri, 22 Apr 2022 17:26:17 +0200 Subject: [PATCH 17/18] fix: format --- .../event/source/polling/PerResourcePollingEventSource.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java index 05dfba033c..f55e7dd05e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java @@ -48,7 +48,8 @@ public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, } public PerResourcePollingEventSource(ResourceFetcher resourceFetcher, - Cache

resourceCache, long period, Class resourceClass, CacheKeyMapper cacheKeyMapper) { + Cache

resourceCache, long period, Class resourceClass, + CacheKeyMapper cacheKeyMapper) { this(resourceFetcher, resourceCache, period, null, resourceClass, cacheKeyMapper); } From 42df322c33b27e9801a3c97f35759a3f65294061 Mon Sep 17 00:00:00 2001 From: csviri Date: Fri, 22 Apr 2022 17:27:23 +0200 Subject: [PATCH 18/18] removed not used class --- .../SchemaPollingResourceFetcher.java | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaPollingResourceFetcher.java diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaPollingResourceFetcher.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaPollingResourceFetcher.java deleted file mode 100644 index 1470d46e49..0000000000 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaPollingResourceFetcher.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.javaoperatorsdk.operator.sample.dependent; - -import java.util.Set; - -import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingEventSource; -import io.javaoperatorsdk.operator.sample.MySQLDbConfig; -import io.javaoperatorsdk.operator.sample.MySQLSchema; -import io.javaoperatorsdk.operator.sample.schema.Schema; -import io.javaoperatorsdk.operator.sample.schema.SchemaService; - -public class SchemaPollingResourceFetcher - implements PerResourcePollingEventSource.ResourceFetcher { - - private final SchemaService schemaService; - - public SchemaPollingResourceFetcher(MySQLDbConfig mySQLDbConfig) { - this.schemaService = new SchemaService(mySQLDbConfig); - } - - @Override - public Set fetchResources(MySQLSchema primaryResource) { - return null; - } -}