From 44ea02e1930797bb426edf925c1570bedb9048b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Sat, 18 Oct 2025 16:52:01 +0200 Subject: [PATCH 01/28] improve: ExternalResourceIDProvider extended to be used in CacheKeyMapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../AbstractPollingDependentResource.java | 10 +++---- .../PerResourcePollingDependentResource.java | 8 +++--- .../external/PollingDependentResource.java | 12 ++++---- .../event/source/CacheKeyMapper.java | 20 +++++++++++-- .../ExternalResourceCachingEventSource.java | 19 ++++++------- .../inbound/CachingInboundEventSource.java | 8 +++--- .../PerResourcePollingConfiguration.java | 8 +++--- ...erResourcePollingConfigurationBuilder.java | 16 +++++------ .../PerResourcePollingEventSource.java | 6 ++-- .../source/polling/PollingConfiguration.java | 8 +++--- .../polling/PollingConfigurationBuilder.java | 11 ++++---- .../source/polling/PollingEventSource.java | 6 ++-- ...xternalResourceCachingEventSourceTest.java | 5 ++-- .../event/source/SampleExternalResource.java | 8 +++++- .../CachingInboundEventSourceTest.java | 5 ++-- .../PerResourcePollingEventSourceTest.java | 13 ++++++--- .../polling/PollingEventSourceTest.java | 4 +-- ...ourcePollingEventSourceTestReconciler.java | 28 ++++++++++--------- .../ExternalBulkDependentResource.java | 2 +- .../external/ExternalResource.java | 9 +++++- .../ExternalStateReconciler.java | 9 ++++-- .../ExternalWithStateDependentResource.java | 3 +- ...ulkDependentResourceExternalWithState.java | 2 +- .../AbstractExternalDependentResource.java | 2 +- ...edExternalDependentResourceReconciler.java | 6 ++-- .../dependent/SchemaDependentResource.java | 2 +- 26 files changed, 136 insertions(+), 94 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 4b255a35d7..22ad401fe7 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 @@ -24,9 +24,9 @@ import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; @Ignore -public abstract class AbstractPollingDependentResource - extends AbstractExternalDependentResource> - implements CacheKeyMapper { +public abstract class AbstractPollingDependentResource + extends AbstractExternalDependentResource> + implements CacheKeyMapper { public static final Duration DEFAULT_POLLING_PERIOD = Duration.ofMillis(5000); private Duration pollingPeriod; @@ -52,7 +52,7 @@ public Duration getPollingPeriod() { // for now dependent resources support event sources only with one owned resource. @Override - public String keyFor(R resource) { - return CacheKeyMapper.singleResourceCacheKeyMapper().keyFor(resource); + public ID keyFor(R resource) { + return CacheKeyMapper.externalIdProviderMapper().keyFor(resource); } } 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 4df1dcf578..18de989bc1 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 @@ -25,8 +25,8 @@ import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingEventSource; @Ignore -public abstract class PerResourcePollingDependentResource - extends AbstractPollingDependentResource +public abstract class PerResourcePollingDependentResource + extends AbstractPollingDependentResource implements PerResourcePollingEventSource.ResourceFetcher { public PerResourcePollingDependentResource() {} @@ -40,13 +40,13 @@ public PerResourcePollingDependentResource(Class resourceType, Duration polli } @Override - protected ExternalResourceCachingEventSource createEventSource( + protected ExternalResourceCachingEventSource createEventSource( EventSourceContext

context) { return new PerResourcePollingEventSource<>( resourceType(), context, - new PerResourcePollingConfigurationBuilder<>(this, getPollingPeriod()) + new PerResourcePollingConfigurationBuilder(this, getPollingPeriod()) .withCacheKeyMapper(this) .withName(name()) .build()); 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 6b80edc61a..339f104a4d 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 @@ -26,25 +26,25 @@ import io.javaoperatorsdk.operator.processing.event.source.polling.PollingEventSource; @Ignore -public abstract class PollingDependentResource - extends AbstractPollingDependentResource +public abstract class PollingDependentResource + extends AbstractPollingDependentResource implements PollingEventSource.GenericResourceFetcher { - private final CacheKeyMapper cacheKeyMapper; + private final CacheKeyMapper cacheKeyMapper; - public PollingDependentResource(Class resourceType, CacheKeyMapper cacheKeyMapper) { + public PollingDependentResource(Class resourceType, CacheKeyMapper cacheKeyMapper) { super(resourceType); this.cacheKeyMapper = cacheKeyMapper; } public PollingDependentResource( - Class resourceType, Duration pollingPeriod, CacheKeyMapper cacheKeyMapper) { + Class resourceType, Duration pollingPeriod, CacheKeyMapper cacheKeyMapper) { super(resourceType, pollingPeriod); this.cacheKeyMapper = cacheKeyMapper; } @Override - protected ExternalResourceCachingEventSource createEventSource( + protected ExternalResourceCachingEventSource createEventSource( EventSourceContext

context) { return new PollingEventSource<>( resourceType(), 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 8038dd1555..6f3b9fd571 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 @@ -15,9 +15,11 @@ */ package io.javaoperatorsdk.operator.processing.event.source; -public interface CacheKeyMapper { +import io.javaoperatorsdk.operator.processing.dependent.ExternalDependentIDProvider; - String keyFor(R resource); +public interface CacheKeyMapper { + + ID keyFor(R resource); /** * Used if a polling event source handles only single secondary resource. See also docs for: @@ -26,7 +28,19 @@ public interface CacheKeyMapper { * @return static id mapper, all resources are mapped for same id. * @param secondary resource type */ - static CacheKeyMapper singleResourceCacheKeyMapper() { + static CacheKeyMapper singleResourceCacheKeyMapper() { return r -> "id"; } + + static CacheKeyMapper externalIdProviderMapper() { + + return r -> { + if (r instanceof ExternalDependentIDProvider externalDependentIDProvider) { + return (ID) externalDependentIDProvider.externalResourceId(); + } else { + throw new IllegalStateException( + "Resource does not implement ExternalDependentIDProvider: " + r.getClass()); + } + }; + } } 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 1c4ce45cb9..2a57d9d682 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 @@ -54,23 +54,23 @@ * @param type of polled external secondary resource * @param

primary resource */ -public abstract class ExternalResourceCachingEventSource +public abstract class ExternalResourceCachingEventSource extends AbstractEventSource implements RecentOperationCacheFiller { private static final Logger log = LoggerFactory.getLogger(ExternalResourceCachingEventSource.class); - protected final CacheKeyMapper cacheKeyMapper; + protected final CacheKeyMapper cacheKeyMapper; - protected Map> cache = new ConcurrentHashMap<>(); + protected Map> cache = new ConcurrentHashMap<>(); protected ExternalResourceCachingEventSource( - Class resourceClass, CacheKeyMapper cacheKeyMapper) { + Class resourceClass, CacheKeyMapper cacheKeyMapper) { this(null, resourceClass, cacheKeyMapper); } protected ExternalResourceCachingEventSource( - String name, Class resourceClass, CacheKeyMapper cacheKeyMapper) { + String name, Class resourceClass, CacheKeyMapper cacheKeyMapper) { super(resourceClass, name); this.cacheKeyMapper = cacheKeyMapper; } @@ -91,7 +91,7 @@ protected synchronized void handleDelete(ResourceID primaryID, R resource) { handleDelete(primaryID, Set.of(cacheKeyMapper.keyFor(resource))); } - protected synchronized void handleDelete(ResourceID primaryID, Set resourceIDs) { + protected synchronized void handleDelete(ResourceID primaryID, Set resourceIDs) { if (!isRunning()) { return; } @@ -146,8 +146,7 @@ && acceptedByFiler(cachedResources, newResourcesMap)) { } } - private boolean acceptedByFiler( - Map cachedResourceMap, Map newResourcesMap) { + private boolean acceptedByFiler(Map cachedResourceMap, Map newResourcesMap) { var addedResources = new HashMap<>(newResourcesMap); addedResources.keySet().removeAll(cachedResourceMap.keySet()); @@ -175,7 +174,7 @@ private boolean acceptedByFiler( return true; } - Map possibleUpdatedResources = new HashMap<>(cachedResourceMap); + Map possibleUpdatedResources = new HashMap<>(cachedResourceMap); possibleUpdatedResources.keySet().retainAll(newResourcesMap.keySet()); possibleUpdatedResources = possibleUpdatedResources.entrySet().stream() @@ -248,7 +247,7 @@ public Optional getSecondaryResource(ResourceID primaryID) { } } - public Map> getCache() { + public Map> getCache() { return Collections.unmodifiableMap(cache); } 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 6c9e6ec660..34d8c51d77 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 @@ -26,8 +26,8 @@ import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.source.ResourceEventAware; -public class CachingInboundEventSource - extends ExternalResourceCachingEventSource implements ResourceEventAware

{ +public class CachingInboundEventSource + extends ExternalResourceCachingEventSource implements ResourceEventAware

{ private final ResourceFetcher resourceFetcher; private final Set fetchedForPrimaries = ConcurrentHashMap.newKeySet(); @@ -35,7 +35,7 @@ public class CachingInboundEventSource public CachingInboundEventSource( ResourceFetcher resourceFetcher, Class resourceClass, - CacheKeyMapper cacheKeyMapper) { + CacheKeyMapper cacheKeyMapper) { super(resourceClass, cacheKeyMapper); this.resourceFetcher = resourceFetcher; } @@ -48,7 +48,7 @@ public void handleResourceEvent(ResourceID primaryID, R resource) { super.handleResources(primaryID, resource); } - public void handleResourceDeleteEvent(ResourceID primaryID, String resourceID) { + public void handleResourceDeleteEvent(ResourceID primaryID, ID resourceID) { super.handleDelete(primaryID, Set.of(resourceID)); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java index a66b92cffd..b05ea90055 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java @@ -24,10 +24,10 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; -public record PerResourcePollingConfiguration( +public record PerResourcePollingConfiguration( String name, ScheduledExecutorService executorService, - CacheKeyMapper cacheKeyMapper, + CacheKeyMapper cacheKeyMapper, PerResourcePollingEventSource.ResourceFetcher resourceFetcher, Predicate

registerPredicate, Duration defaultPollingPeriod) { @@ -37,7 +37,7 @@ public record PerResourcePollingConfiguration( public PerResourcePollingConfiguration( String name, ScheduledExecutorService executorService, - CacheKeyMapper cacheKeyMapper, + CacheKeyMapper cacheKeyMapper, PerResourcePollingEventSource.ResourceFetcher resourceFetcher, Predicate

registerPredicate, Duration defaultPollingPeriod) { @@ -47,7 +47,7 @@ public PerResourcePollingConfiguration( ? new ScheduledThreadPoolExecutor(DEFAULT_EXECUTOR_THREAD_NUMBER) : executorService; this.cacheKeyMapper = - cacheKeyMapper == null ? CacheKeyMapper.singleResourceCacheKeyMapper() : cacheKeyMapper; + cacheKeyMapper == null ? CacheKeyMapper.externalIdProviderMapper() : cacheKeyMapper; this.resourceFetcher = Objects.requireNonNull(resourceFetcher); this.registerPredicate = registerPredicate; this.defaultPollingPeriod = defaultPollingPeriod; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfigurationBuilder.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfigurationBuilder.java index 13a70bd9f2..7c15904742 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfigurationBuilder.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfigurationBuilder.java @@ -22,7 +22,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; -public final class PerResourcePollingConfigurationBuilder { +public final class PerResourcePollingConfigurationBuilder { private final Duration defaultPollingPeriod; private final PerResourcePollingEventSource.ResourceFetcher resourceFetcher; @@ -30,7 +30,7 @@ public final class PerResourcePollingConfigurationBuilder registerPredicate; private ScheduledExecutorService executorService; - private CacheKeyMapper cacheKeyMapper; + private CacheKeyMapper cacheKeyMapper; public PerResourcePollingConfigurationBuilder( PerResourcePollingEventSource.ResourceFetcher resourceFetcher, @@ -40,30 +40,30 @@ public PerResourcePollingConfigurationBuilder( } @SuppressWarnings("unused") - public PerResourcePollingConfigurationBuilder withExecutorService( + public PerResourcePollingConfigurationBuilder withExecutorService( ScheduledExecutorService executorService) { this.executorService = executorService; return this; } - public PerResourcePollingConfigurationBuilder withRegisterPredicate( + public PerResourcePollingConfigurationBuilder withRegisterPredicate( Predicate

registerPredicate) { this.registerPredicate = registerPredicate; return this; } - public PerResourcePollingConfigurationBuilder withCacheKeyMapper( - CacheKeyMapper cacheKeyMapper) { + public PerResourcePollingConfigurationBuilder withCacheKeyMapper( + CacheKeyMapper cacheKeyMapper) { this.cacheKeyMapper = cacheKeyMapper; return this; } - public PerResourcePollingConfigurationBuilder withName(String name) { + public PerResourcePollingConfigurationBuilder withName(String name) { this.name = name; return this; } - public PerResourcePollingConfiguration build() { + public PerResourcePollingConfiguration build() { return new PerResourcePollingConfiguration<>( name, executorService, 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 2827c0bac0..d0e8116cb4 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,8 +48,8 @@ * @param the resource polled by the event source * @param

related custom resource */ -public class PerResourcePollingEventSource - extends ExternalResourceCachingEventSource implements ResourceEventAware

{ +public class PerResourcePollingEventSource + extends ExternalResourceCachingEventSource implements ResourceEventAware

{ private static final Logger log = LoggerFactory.getLogger(PerResourcePollingEventSource.class); @@ -65,7 +65,7 @@ public class PerResourcePollingEventSource public PerResourcePollingEventSource( Class resourceClass, EventSourceContext

context, - PerResourcePollingConfiguration config) { + PerResourcePollingConfiguration config) { super(config.name(), resourceClass, config.cacheKeyMapper()); this.primaryResourceCache = context.getPrimaryCache(); this.resourceFetcher = config.resourceFetcher(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java index 56b6261a88..c93b5e43d7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java @@ -20,21 +20,21 @@ import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; -public record PollingConfiguration( +public record PollingConfiguration( String name, PollingEventSource.GenericResourceFetcher genericResourceFetcher, Duration period, - CacheKeyMapper cacheKeyMapper) { + CacheKeyMapper cacheKeyMapper) { public PollingConfiguration( String name, PollingEventSource.GenericResourceFetcher genericResourceFetcher, Duration period, - CacheKeyMapper cacheKeyMapper) { + CacheKeyMapper cacheKeyMapper) { this.name = name; this.genericResourceFetcher = Objects.requireNonNull(genericResourceFetcher); this.period = period; this.cacheKeyMapper = - cacheKeyMapper == null ? CacheKeyMapper.singleResourceCacheKeyMapper() : cacheKeyMapper; + cacheKeyMapper == null ? CacheKeyMapper.externalIdProviderMapper() : cacheKeyMapper; } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java index 8d922080eb..783dca99da 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java @@ -19,10 +19,10 @@ import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; -public final class PollingConfigurationBuilder { +public final class PollingConfigurationBuilder { private final Duration period; private final PollingEventSource.GenericResourceFetcher genericResourceFetcher; - private CacheKeyMapper cacheKeyMapper; + private CacheKeyMapper cacheKeyMapper; private String name; public PollingConfigurationBuilder( @@ -31,17 +31,18 @@ public PollingConfigurationBuilder( this.period = period; } - public PollingConfigurationBuilder withCacheKeyMapper(CacheKeyMapper cacheKeyMapper) { + public PollingConfigurationBuilder withCacheKeyMapper( + CacheKeyMapper cacheKeyMapper) { this.cacheKeyMapper = cacheKeyMapper; return this; } - public PollingConfigurationBuilder withName(String name) { + public PollingConfigurationBuilder withName(String name) { this.name = name; return this; } - public PollingConfiguration build() { + public PollingConfiguration build() { return new PollingConfiguration<>(name, genericResourceFetcher, period, cacheKeyMapper); } } 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 04548e1a11..be3b397343 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 @@ -57,8 +57,8 @@ * @param type of the polled resource * @param

primary resource type */ -public class PollingEventSource - extends ExternalResourceCachingEventSource { +public class PollingEventSource + extends ExternalResourceCachingEventSource { private static final Logger log = LoggerFactory.getLogger(PollingEventSource.class); @@ -67,7 +67,7 @@ public class PollingEventSource private final Duration period; private final AtomicBoolean healthy = new AtomicBoolean(true); - public PollingEventSource(Class resourceClass, PollingConfiguration config) { + public PollingEventSource(Class resourceClass, PollingConfiguration config) { super(config.name(), resourceClass, config.cacheKeyMapper()); this.genericResourceFetcher = config.genericResourceFetcher(); this.period = config.period(); 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 0fc5495545..889cc4da75 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 @@ -30,7 +30,8 @@ class ExternalResourceCachingEventSourceTest extends AbstractEventSourceTestBase< - ExternalResourceCachingEventSource, EventHandler> { + ExternalResourceCachingEventSource, + EventHandler> { @BeforeEach public void setup() { @@ -211,7 +212,7 @@ void genericFilteringEvents() { } public static class TestExternalCachingEventSource - extends ExternalResourceCachingEventSource { + extends ExternalResourceCachingEventSource { public TestExternalCachingEventSource() { super(SampleExternalResource.class, SampleExternalResource::getName); } 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 a6cebc0916..62b3403f2c 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 @@ -18,9 +18,10 @@ import java.io.Serializable; import java.util.Objects; +import io.javaoperatorsdk.operator.processing.dependent.ExternalDependentIDProvider; import io.javaoperatorsdk.operator.processing.event.ResourceID; -public class SampleExternalResource implements Serializable { +public class SampleExternalResource implements Serializable, ExternalDependentIDProvider { public static final String DEFAULT_VALUE_1 = "value1"; public static final String DEFAULT_VALUE_2 = "value2"; @@ -81,4 +82,9 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(name, value); } + + @Override + public String externalResourceId() { + return name; + } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSourceTest.java index e468e20561..c02f6a288c 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSourceTest.java @@ -39,7 +39,8 @@ class CachingInboundEventSourceTest extends AbstractEventSourceTestBase< - CachingInboundEventSource, EventHandler> { + CachingInboundEventSource, + EventHandler> { @SuppressWarnings("unchecked") private final CachingInboundEventSource.ResourceFetcher< @@ -47,7 +48,7 @@ class CachingInboundEventSourceTest supplier = mock(CachingInboundEventSource.ResourceFetcher.class); private final TestCustomResource testCustomResource = TestUtils.testCustomResource(); - private final CacheKeyMapper cacheKeyMapper = + private final CacheKeyMapper cacheKeyMapper = r -> r.getName() + "#" + r.getValue(); @BeforeEach 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 cc5958cef4..0a2c413d3b 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 @@ -40,7 +40,8 @@ class PerResourcePollingEventSourceTest extends AbstractEventSourceTestBase< - PerResourcePollingEventSource, EventHandler> { + PerResourcePollingEventSource, + EventHandler> { public static final int PERIOD = 150; @@ -66,7 +67,9 @@ public void setup() { new PerResourcePollingEventSource<>( SampleExternalResource.class, context, - new PerResourcePollingConfigurationBuilder<>(supplier, Duration.ofMillis(PERIOD)) + new PerResourcePollingConfigurationBuilder< + SampleExternalResource, TestCustomResource, String>( + supplier, Duration.ofMillis(PERIOD)) .withCacheKeyMapper(r -> r.getName() + "#" + r.getValue()) .build())); } @@ -91,10 +94,12 @@ void registeringTaskOnAPredicate() { new PerResourcePollingEventSource<>( SampleExternalResource.class, context, - new PerResourcePollingConfigurationBuilder<>(supplier, Duration.ofMillis(PERIOD)) + new PerResourcePollingConfigurationBuilder< + SampleExternalResource, TestCustomResource, String>( + supplier, Duration.ofMillis(PERIOD)) .withRegisterPredicate( testCustomResource -> testCustomResource.getMetadata().getGeneration() > 1) - .withCacheKeyMapper(CacheKeyMapper.singleResourceCacheKeyMapper()) + .withCacheKeyMapper(CacheKeyMapper.externalIdProviderMapper()) .build())); source.onResourceCreated(testCustomResource); 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 923a4915b9..fbf657ba0b 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 @@ -37,7 +37,7 @@ class PollingEventSourceTest extends AbstractEventSourceTestBase< - PollingEventSource, EventHandler> { + PollingEventSource, EventHandler> { public static final int DEFAULT_WAIT_PERIOD = 100; public static final Duration POLL_PERIOD = Duration.ofMillis(30L); @@ -46,7 +46,7 @@ class PollingEventSourceTest private final PollingEventSource.GenericResourceFetcher resourceFetcher = mock(PollingEventSource.GenericResourceFetcher.class); - private final PollingEventSource pollingEventSource = + private final PollingEventSource pollingEventSource = new PollingEventSource<>( SampleExternalResource.class, new PollingConfiguration<>( diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java index c2233d931b..4e4334cc16 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java @@ -52,19 +52,21 @@ public UpdateControl reconcile( @Override public List> prepareEventSources( EventSourceContext context) { - PerResourcePollingEventSource eventSource = - new PerResourcePollingEventSource<>( - String.class, - context, - new PerResourcePollingConfigurationBuilder<>( - (PerResourceEventSourceCustomResource resource) -> { - numberOfFetchExecutions.putIfAbsent(resource.getMetadata().getName(), 0); - numberOfFetchExecutions.compute( - resource.getMetadata().getName(), (s, v) -> v + 1); - return Set.of(UUID.randomUUID().toString()); - }, - Duration.ofMillis(POLL_PERIOD)) - .build()); + PerResourcePollingEventSource + eventSource = + new PerResourcePollingEventSource<>( + String.class, + context, + new PerResourcePollingConfigurationBuilder< + String, PerResourceEventSourceCustomResource, String>( + (PerResourceEventSourceCustomResource resource) -> { + numberOfFetchExecutions.putIfAbsent(resource.getMetadata().getName(), 0); + numberOfFetchExecutions.compute( + resource.getMetadata().getName(), (s, v) -> v + 1); + return Set.of(UUID.randomUUID().toString()); + }, + Duration.ofMillis(POLL_PERIOD)) + .build()); return List.of(eventSource); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java index d6f3893674..dda70286d8 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java @@ -31,7 +31,7 @@ import io.javaoperatorsdk.operator.processing.event.ResourceID; public class ExternalBulkDependentResource - extends PollingDependentResource + extends PollingDependentResource implements BulkDependentResource, Creator, Deleter, diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalResource.java index fff4f70656..9cf3132635 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalResource.java @@ -17,7 +17,9 @@ import java.util.Objects; -public class ExternalResource { +import io.javaoperatorsdk.operator.processing.dependent.ExternalDependentIDProvider; + +public class ExternalResource implements ExternalDependentIDProvider { private final String id; private final String data; @@ -47,4 +49,9 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash(id, data); } + + @Override + public String externalResourceId() { + return id; + } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateReconciler.java index d1a03b0c8e..de485cfc4e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateReconciler.java @@ -56,7 +56,7 @@ public class ExternalStateReconciler private final ExternalIDGenServiceMock externalService = ExternalIDGenServiceMock.getInstance(); InformerEventSource configMapEventSource; - PerResourcePollingEventSource + PerResourcePollingEventSource externalResourceEventSource; @Override @@ -158,10 +158,13 @@ public List> prepareEventSources( return externalResource.map(Set::of).orElseGet(Collections::emptySet); }; externalResourceEventSource = - new PerResourcePollingEventSource<>( + new PerResourcePollingEventSource( ExternalResource.class, context, - new PerResourcePollingConfigurationBuilder<>(fetcher, Duration.ofMillis(300L)).build()); + new PerResourcePollingConfigurationBuilder< + ExternalResource, ExternalStateCustomResource, String>( + fetcher, Duration.ofMillis(300L)) + .build()); return Arrays.asList(configMapEventSource, externalResourceEventSource); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalWithStateDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalWithStateDependentResource.java index c264ff52c7..e31578a843 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalWithStateDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalWithStateDependentResource.java @@ -33,7 +33,8 @@ import io.javaoperatorsdk.operator.support.ExternalResource; public class ExternalWithStateDependentResource - extends PerResourcePollingDependentResource + extends PerResourcePollingDependentResource< + ExternalResource, ExternalStateCustomResource, String> implements DependentResourceWithExplicitState< ExternalResource, ExternalStateCustomResource, ConfigMap>, Updater { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java index 696b02c01e..41a4db2f02 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java @@ -34,7 +34,7 @@ public class BulkDependentResourceExternalWithState extends PerResourcePollingDependentResource< - ExternalResource, ExternalStateBulkDependentCustomResource> + ExternalResource, ExternalStateBulkDependentCustomResource, String> implements BulkDependentResource, CRUDBulkDependentResource, DependentResourceWithExplicitState< diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java index 743e6b94f9..7c39a7db9a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java @@ -30,7 +30,7 @@ public abstract class AbstractExternalDependentResource extends PollingDependentResource< - ExternalResource, MultipleManagedExternalDependentResourceCustomResource> + ExternalResource, MultipleManagedExternalDependentResourceCustomResource, String> implements Creator, Updater, Deleter { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java index 9380895105..66939a3c49 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java @@ -91,11 +91,13 @@ public int getNumberOfExecutions() { return res; }; - PollingEventSource + PollingEventSource< + ExternalResource, MultipleManagedExternalDependentResourceCustomResource, String> pollingEventSource = new PollingEventSource<>( ExternalResource.class, - new PollingConfigurationBuilder<>(fetcher, Duration.ofMillis(1000L)) + new PollingConfigurationBuilder( + fetcher, Duration.ofMillis(1000L)) .withName(EVENT_SOURCE_NAME) .withCacheKeyMapper(ExternalResource::getId) .build()); 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 4c510edee5..6400d312c7 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 @@ -58,7 +58,7 @@ with = ResourcePollerConfig.class, converter = ResourcePollerConfigConverter.class) public class SchemaDependentResource - extends PerResourcePollingDependentResource + extends PerResourcePollingDependentResource implements ConfiguredDependentResource, Creator, Deleter { From 91ea413830fe7b8cb8cfbabfcc592796fa6778f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Sun, 19 Oct 2025 19:09:20 +0200 Subject: [PATCH 02/28] wip --- .../dependent-resources.md | 6 +++--- ...dentIDProvider.java => ResourceIDProvider.java} | 14 ++++++++------ .../AbstractExternalDependentResource.java | 8 +++----- .../external/AbstractPollingDependentResource.java | 2 +- .../processing/event/source/CacheKeyMapper.java | 13 ++++++------- .../source/ExternalResourceCachingEventSource.java | 6 ++++++ .../polling/PerResourcePollingConfiguration.java | 2 +- .../event/source/polling/PollingConfiguration.java | 2 +- .../event/source/SampleExternalResource.java | 6 +++--- .../polling/PerResourcePollingEventSourceTest.java | 2 +- ...erResourcePollingEventSourceTestReconciler.java | 2 ++ .../bulkdependent/external/ExternalResource.java | 6 +++--- .../operator/support/ExternalResource.java | 6 +++--- .../operator/sample/schema/Schema.java | 6 +++--- 14 files changed, 44 insertions(+), 37 deletions(-) rename operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/{dependent/ExternalDependentIDProvider.java => ResourceIDProvider.java} (61%) diff --git a/docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md b/docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md index 8a575f716f..a73d600609 100644 --- a/docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md +++ b/docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md @@ -370,13 +370,13 @@ or we can use a matcher based SSA in most of the cases if the resource is manage Unfortunately this is not true for external resources. So to make sure we are selecting the target resources from an event source, we provide a [mechanism](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java#L114-L138) that helps with that logic. -Your POJO representing an external resource can implement [`ExternalResourceIDProvider`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ExternalDependentIDProvider.java) : +Your POJO representing an external resource can implement [`ResourceIDProvider`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java) : ```java -public interface ExternalDependentIDProvider { +public interface ResourceIDProvider { - T externalResourceId(); + T resourceId(); } ``` diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ExternalDependentIDProvider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java similarity index 61% rename from operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ExternalDependentIDProvider.java rename to operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java index b6c755178d..f2e5e9728b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ExternalDependentIDProvider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java @@ -13,16 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.javaoperatorsdk.operator.processing.dependent; +package io.javaoperatorsdk.operator.processing; /** - * Provides the identifier for an object that represents an external resource. This ID is used to - * select target resource for a dependent resource from the resources returned by `{@link - * io.javaoperatorsdk.operator.api.reconciler.Context#getSecondaryResources(Class)}`. + * Provides the identifier for an object that represents resource. This ID is used to select target + * external resource for a dependent resource from the resources returned by `{@link + * io.javaoperatorsdk.operator.api.reconciler.Context#getSecondaryResources(Class)}`. But also for + * {@link io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper} for event sources in + * external resources * * @param */ -public interface ExternalDependentIDProvider { +public interface ResourceIDProvider { - T externalResourceId(); + T resourceId(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java index 2c5be82288..f20a842cad 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java @@ -22,6 +22,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationCacheFiller; +import io.javaoperatorsdk.operator.processing.ResourceIDProvider; import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.EventSource; @@ -131,14 +132,11 @@ protected Optional selectTargetSecondaryResource( Set secondaryResources, P primary, Context

context) { R desired = desired(primary, context); List targetResources; - if (desired instanceof ExternalDependentIDProvider desiredWithId) { + if (desired instanceof ResourceIDProvider desiredWithId) { targetResources = secondaryResources.stream() .filter( - r -> - ((ExternalDependentIDProvider) r) - .externalResourceId() - .equals(desiredWithId.externalResourceId())) + r -> ((ResourceIDProvider) r).resourceId().equals(desiredWithId.resourceId())) .toList(); } else { throw new IllegalStateException( 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 22ad401fe7..acf8580e27 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 @@ -53,6 +53,6 @@ public Duration getPollingPeriod() { // for now dependent resources support event sources only with one owned resource. @Override public ID keyFor(R resource) { - return CacheKeyMapper.externalIdProviderMapper().keyFor(resource); + return CacheKeyMapper.resourceIdProviderMapper().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 6f3b9fd571..069a06d3fc 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 @@ -15,15 +15,15 @@ */ package io.javaoperatorsdk.operator.processing.event.source; -import io.javaoperatorsdk.operator.processing.dependent.ExternalDependentIDProvider; +import io.javaoperatorsdk.operator.processing.ResourceIDProvider; public interface CacheKeyMapper { ID keyFor(R resource); /** - * Used if a polling event source handles only single secondary resource. See also docs for: - * {@link ExternalResourceCachingEventSource} + * Used if a polling event source handles only single secondary resource and the id is String. See + * also docs for: {@link ExternalResourceCachingEventSource} * * @return static id mapper, all resources are mapped for same id. * @param secondary resource type @@ -32,11 +32,10 @@ static CacheKeyMapper singleResourceCacheKeyMapper() { return r -> "id"; } - static CacheKeyMapper externalIdProviderMapper() { - + static CacheKeyMapper resourceIdProviderMapper() { return r -> { - if (r instanceof ExternalDependentIDProvider externalDependentIDProvider) { - return (ID) externalDependentIDProvider.externalResourceId(); + if (r instanceof ResourceIDProvider resourceIDProvider) { + return (ID) resourceIDProvider.resourceId(); } else { throw new IllegalStateException( "Resource does not implement ExternalDependentIDProvider: " + r.getClass()); 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 2a57d9d682..eeaf766459 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 @@ -32,6 +32,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationCacheFiller; +import io.javaoperatorsdk.operator.processing.ResourceIDProvider; import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.ResourceID; @@ -72,6 +73,11 @@ protected ExternalResourceCachingEventSource( protected ExternalResourceCachingEventSource( String name, Class resourceClass, CacheKeyMapper cacheKeyMapper) { super(resourceClass, name); + if (cacheKeyMapper == CacheKeyMapper.resourceIdProviderMapper() + && !ResourceIDProvider.class.isAssignableFrom(resourceClass)) { + throw new IllegalArgumentException( + "resource class is not a " + ResourceIDProvider.class.getSimpleName()); + } this.cacheKeyMapper = cacheKeyMapper; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java index b05ea90055..73546dfd4f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java @@ -47,7 +47,7 @@ public PerResourcePollingConfiguration( ? new ScheduledThreadPoolExecutor(DEFAULT_EXECUTOR_THREAD_NUMBER) : executorService; this.cacheKeyMapper = - cacheKeyMapper == null ? CacheKeyMapper.externalIdProviderMapper() : cacheKeyMapper; + cacheKeyMapper == null ? CacheKeyMapper.resourceIdProviderMapper() : cacheKeyMapper; this.resourceFetcher = Objects.requireNonNull(resourceFetcher); this.registerPredicate = registerPredicate; this.defaultPollingPeriod = defaultPollingPeriod; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java index c93b5e43d7..e48d4fd3e1 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java @@ -35,6 +35,6 @@ public PollingConfiguration( this.genericResourceFetcher = Objects.requireNonNull(genericResourceFetcher); this.period = period; this.cacheKeyMapper = - cacheKeyMapper == null ? CacheKeyMapper.externalIdProviderMapper() : cacheKeyMapper; + cacheKeyMapper == null ? CacheKeyMapper.resourceIdProviderMapper() : cacheKeyMapper; } } 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 62b3403f2c..4abac62b2e 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 @@ -18,10 +18,10 @@ import java.io.Serializable; import java.util.Objects; -import io.javaoperatorsdk.operator.processing.dependent.ExternalDependentIDProvider; +import io.javaoperatorsdk.operator.processing.ResourceIDProvider; import io.javaoperatorsdk.operator.processing.event.ResourceID; -public class SampleExternalResource implements Serializable, ExternalDependentIDProvider { +public class SampleExternalResource implements Serializable, ResourceIDProvider { public static final String DEFAULT_VALUE_1 = "value1"; public static final String DEFAULT_VALUE_2 = "value2"; @@ -84,7 +84,7 @@ public int hashCode() { } @Override - public String externalResourceId() { + public String resourceId() { return name; } } 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 0a2c413d3b..dbe93da915 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 @@ -99,7 +99,7 @@ void registeringTaskOnAPredicate() { supplier, Duration.ofMillis(PERIOD)) .withRegisterPredicate( testCustomResource -> testCustomResource.getMetadata().getGeneration() > 1) - .withCacheKeyMapper(CacheKeyMapper.externalIdProviderMapper()) + .withCacheKeyMapper(CacheKeyMapper.resourceIdProviderMapper()) .build())); source.onResourceCreated(testCustomResource); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java index 4e4334cc16..91aa487063 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java @@ -27,6 +27,7 @@ import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingConfigurationBuilder; import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingEventSource; @@ -66,6 +67,7 @@ public List> prepareEventSo return Set.of(UUID.randomUUID().toString()); }, Duration.ofMillis(POLL_PERIOD)) + .withCacheKeyMapper(CacheKeyMapper.singleResourceCacheKeyMapper()) .build()); return List.of(eventSource); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalResource.java index 9cf3132635..a9fb27e56d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalResource.java @@ -17,9 +17,9 @@ import java.util.Objects; -import io.javaoperatorsdk.operator.processing.dependent.ExternalDependentIDProvider; +import io.javaoperatorsdk.operator.processing.ResourceIDProvider; -public class ExternalResource implements ExternalDependentIDProvider { +public class ExternalResource implements ResourceIDProvider { private final String id; private final String data; @@ -51,7 +51,7 @@ public int hashCode() { } @Override - public String externalResourceId() { + public String resourceId() { return id; } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalResource.java index f281fe95db..d581f3c624 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalResource.java @@ -18,10 +18,10 @@ import java.util.Objects; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.dependent.ExternalDependentIDProvider; +import io.javaoperatorsdk.operator.processing.ResourceIDProvider; import io.javaoperatorsdk.operator.processing.event.ResourceID; -public class ExternalResource implements ExternalDependentIDProvider { +public class ExternalResource implements ResourceIDProvider { public static final String EXTERNAL_RESOURCE_NAME_DELIMITER = "#"; @@ -83,7 +83,7 @@ public static String toExternalResourceId(HasMetadata primary) { } @Override - public String externalResourceId() { + public String resourceId() { return id; } } diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/schema/Schema.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/schema/Schema.java index baaa829534..a7c001ffe5 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/schema/Schema.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/schema/Schema.java @@ -18,9 +18,9 @@ import java.io.Serializable; import java.util.Objects; -import io.javaoperatorsdk.operator.processing.dependent.ExternalDependentIDProvider; +import io.javaoperatorsdk.operator.processing.ResourceIDProvider; -public class Schema implements Serializable, ExternalDependentIDProvider { +public class Schema implements Serializable, ResourceIDProvider { private final String name; private final String characterSet; @@ -57,7 +57,7 @@ public String toString() { } @Override - public String externalResourceId() { + public String resourceId() { return name; } } From 1df9117455286c1d40e4c56fe65ada31bea30d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Thu, 23 Oct 2025 16:20:17 +0200 Subject: [PATCH 03/28] improve: ResourceIDMapper and ResourceIDProvider for external resources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Will check if this could be elegantly extended to bulk resources Signed-off-by: Attila Mészáros --- ...heKeyMapper.java => ResourceIDMapper.java} | 12 ++++---- .../processing/ResourceIDProvider.java | 3 +- .../AbstractExternalDependentResource.java | 30 +++++++++++-------- .../AbstractPollingDependentResource.java | 11 ++----- .../PerResourcePollingDependentResource.java | 2 +- .../external/PollingDependentResource.java | 14 ++++----- .../ExternalResourceCachingEventSource.java | 23 +++++++------- .../inbound/CachingInboundEventSource.java | 6 ++-- .../PerResourcePollingConfiguration.java | 10 +++---- ...erResourcePollingConfigurationBuilder.java | 12 ++++---- .../PerResourcePollingEventSource.java | 2 +- .../source/polling/PollingConfiguration.java | 10 +++---- .../polling/PollingConfigurationBuilder.java | 10 +++---- .../source/polling/PollingEventSource.java | 2 +- .../CachingInboundEventSourceTest.java | 10 +++---- .../PerResourcePollingEventSourceTest.java | 6 ++-- ...ourcePollingEventSourceTestReconciler.java | 4 +-- ...ulkDependentResourceExternalWithState.java | 6 +--- 18 files changed, 83 insertions(+), 90 deletions(-) rename operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/{event/source/CacheKeyMapper.java => ResourceIDMapper.java} (77%) 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/ResourceIDMapper.java similarity index 77% rename from operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java rename to operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java index 069a06d3fc..ebad96116a 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/ResourceIDMapper.java @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.javaoperatorsdk.operator.processing.event.source; +package io.javaoperatorsdk.operator.processing; -import io.javaoperatorsdk.operator.processing.ResourceIDProvider; +import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; -public interface CacheKeyMapper { +public interface ResourceIDMapper { - ID keyFor(R resource); + ID idFor(R resource); /** * Used if a polling event source handles only single secondary resource and the id is String. See @@ -28,11 +28,11 @@ public interface CacheKeyMapper { * @return static id mapper, all resources are mapped for same id. * @param secondary resource type */ - static CacheKeyMapper singleResourceCacheKeyMapper() { + static ResourceIDMapper singleResourceCacheKeyMapper() { return r -> "id"; } - static CacheKeyMapper resourceIdProviderMapper() { + static ResourceIDMapper resourceIdProviderMapper() { return r -> { if (r instanceof ResourceIDProvider resourceIDProvider) { return (ID) resourceIDProvider.resourceId(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java index f2e5e9728b..dcab1be92a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java @@ -19,8 +19,7 @@ * Provides the identifier for an object that represents resource. This ID is used to select target * external resource for a dependent resource from the resources returned by `{@link * io.javaoperatorsdk.operator.api.reconciler.Context#getSecondaryResources(Class)}`. But also for - * {@link io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper} for event sources in - * external resources + * {@link ResourceIDMapper} for event sources in external resources * * @param */ diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java index f20a842cad..6d0d76e9c5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java @@ -22,20 +22,22 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationCacheFiller; -import io.javaoperatorsdk.operator.processing.ResourceIDProvider; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; public abstract class AbstractExternalDependentResource< - R, P extends HasMetadata, T extends EventSource> + R, P extends HasMetadata, T extends EventSource, ID> extends AbstractEventSourceHolderDependentResource { private final boolean isDependentResourceWithExplicitState = this instanceof DependentResourceWithExplicitState; private final boolean isBulkDependentResource = this instanceof BulkDependentResource; + protected ResourceIDMapper resourceIDMapper = ResourceIDMapper.resourceIdProviderMapper(); + @SuppressWarnings("rawtypes") private DependentResourceWithExplicitState dependentResourceWithExplicitState; @@ -132,21 +134,23 @@ protected Optional selectTargetSecondaryResource( Set secondaryResources, P primary, Context

context) { R desired = desired(primary, context); List targetResources; - if (desired instanceof ResourceIDProvider desiredWithId) { - targetResources = - secondaryResources.stream() - .filter( - r -> ((ResourceIDProvider) r).resourceId().equals(desiredWithId.resourceId())) - .toList(); - } else { - throw new IllegalStateException( - "Either implement ExternalDependentIDProvider or override this " - + " (selectTargetSecondaryResource) method."); - } + var desiredID = resourceIDMapper.idFor(desired); + targetResources = + secondaryResources.stream() + .filter(r -> resourceIDMapper.idFor(r).equals(desiredID)) + .toList(); if (targetResources.size() > 1) { throw new IllegalStateException( "More than one secondary resource related to primary: " + targetResources); } return targetResources.isEmpty() ? Optional.empty() : Optional.of(targetResources.get(0)); } + + public ResourceIDMapper getResourceIDMapper() { + return resourceIDMapper; + } + + public void setResourceIDMapper(ResourceIDMapper resourceIDMapper) { + this.resourceIDMapper = resourceIDMapper; + } } 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 acf8580e27..1776194e33 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 @@ -20,13 +20,12 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.Ignore; import io.javaoperatorsdk.operator.processing.dependent.AbstractExternalDependentResource; -import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; @Ignore public abstract class AbstractPollingDependentResource - extends AbstractExternalDependentResource> - implements CacheKeyMapper { + extends AbstractExternalDependentResource< + R, P, ExternalResourceCachingEventSource, ID> { public static final Duration DEFAULT_POLLING_PERIOD = Duration.ofMillis(5000); private Duration pollingPeriod; @@ -49,10 +48,4 @@ public void setPollingPeriod(Duration pollingPeriod) { public Duration getPollingPeriod() { return pollingPeriod; } - - // for now dependent resources support event sources only with one owned resource. - @Override - public ID keyFor(R resource) { - return CacheKeyMapper.resourceIdProviderMapper().keyFor(resource); - } } 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 18de989bc1..15ac34aa6e 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 @@ -47,7 +47,7 @@ protected ExternalResourceCachingEventSource createEventSource( resourceType(), context, new PerResourcePollingConfigurationBuilder(this, getPollingPeriod()) - .withCacheKeyMapper(this) + .withResourceIDMapper(getResourceIDMapper()) .withName(name()) .build()); } 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 339f104a4d..1abeeb1ef2 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 @@ -20,7 +20,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.api.reconciler.Ignore; -import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.source.polling.PollingConfiguration; import io.javaoperatorsdk.operator.processing.event.source.polling.PollingEventSource; @@ -30,17 +30,17 @@ public abstract class PollingDependentResource extends AbstractPollingDependentResource implements PollingEventSource.GenericResourceFetcher { - private final CacheKeyMapper cacheKeyMapper; + private final ResourceIDMapper resourceIDMapper; - public PollingDependentResource(Class resourceType, CacheKeyMapper cacheKeyMapper) { + public PollingDependentResource(Class resourceType, ResourceIDMapper resourceIDMapper) { super(resourceType); - this.cacheKeyMapper = cacheKeyMapper; + this.resourceIDMapper = resourceIDMapper; } public PollingDependentResource( - Class resourceType, Duration pollingPeriod, CacheKeyMapper cacheKeyMapper) { + Class resourceType, Duration pollingPeriod, ResourceIDMapper resourceIDMapper) { super(resourceType, pollingPeriod); - this.cacheKeyMapper = cacheKeyMapper; + this.resourceIDMapper = resourceIDMapper; } @Override @@ -48,6 +48,6 @@ protected ExternalResourceCachingEventSource createEventSource( EventSourceContext

context) { return new PollingEventSource<>( resourceType(), - new PollingConfiguration<>(name(), this, getPollingPeriod(), cacheKeyMapper)); + new PollingConfiguration<>(name(), this, getPollingPeriod(), resourceIDMapper)); } } 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 eeaf766459..259d6e6f0b 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 @@ -32,6 +32,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationCacheFiller; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; import io.javaoperatorsdk.operator.processing.ResourceIDProvider; import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.ResourceID; @@ -61,24 +62,24 @@ public abstract class ExternalResourceCachingEventSource cacheKeyMapper; + protected final ResourceIDMapper resourceIDMapper; protected Map> cache = new ConcurrentHashMap<>(); protected ExternalResourceCachingEventSource( - Class resourceClass, CacheKeyMapper cacheKeyMapper) { - this(null, resourceClass, cacheKeyMapper); + Class resourceClass, ResourceIDMapper resourceIDMapper) { + this(null, resourceClass, resourceIDMapper); } protected ExternalResourceCachingEventSource( - String name, Class resourceClass, CacheKeyMapper cacheKeyMapper) { + String name, Class resourceClass, ResourceIDMapper resourceIDMapper) { super(resourceClass, name); - if (cacheKeyMapper == CacheKeyMapper.resourceIdProviderMapper() + if (resourceIDMapper == ResourceIDMapper.resourceIdProviderMapper() && !ResourceIDProvider.class.isAssignableFrom(resourceClass)) { throw new IllegalArgumentException( "resource class is not a " + ResourceIDProvider.class.getSimpleName()); } - this.cacheKeyMapper = cacheKeyMapper; + this.resourceIDMapper = resourceIDMapper; } protected synchronized void handleDelete(ResourceID primaryID) { @@ -90,11 +91,11 @@ protected synchronized void handleDelete(ResourceID primaryID) { protected synchronized void handleDeletes(ResourceID primaryID, Set resource) { handleDelete( - primaryID, resource.stream().map(cacheKeyMapper::keyFor).collect(Collectors.toSet())); + primaryID, resource.stream().map(resourceIDMapper::idFor).collect(Collectors.toSet())); } protected synchronized void handleDelete(ResourceID primaryID, R resource) { - handleDelete(primaryID, Set.of(cacheKeyMapper.keyFor(resource))); + handleDelete(primaryID, Set.of(resourceIDMapper.idFor(resource))); } protected synchronized void handleDelete(ResourceID primaryID, Set resourceIDs) { @@ -143,7 +144,7 @@ protected synchronized void handleResources( cachedResources = Collections.emptyMap(); } var newResourcesMap = - newResources.stream().collect(Collectors.toMap(cacheKeyMapper::keyFor, r -> r)); + newResources.stream().collect(Collectors.toMap(resourceIDMapper::idFor, r -> r)); cache.put(primaryID, newResourcesMap); if (propagateEvent && !newResourcesMap.equals(cachedResources) @@ -205,7 +206,7 @@ private boolean acceptedByGenericFiler(R resource) { @Override public synchronized void handleRecentResourceCreate(ResourceID primaryID, R resource) { var actualValues = cache.get(primaryID); - var resourceId = cacheKeyMapper.keyFor(resource); + var resourceId = resourceIDMapper.idFor(resource); if (actualValues == null) { actualValues = new HashMap<>(); cache.put(primaryID, actualValues); @@ -220,7 +221,7 @@ public synchronized void handleRecentResourceUpdate( ResourceID primaryID, R resource, R previousVersionOfResource) { var actualValues = cache.get(primaryID); if (actualValues != null) { - var resourceId = cacheKeyMapper.keyFor(resource); + var resourceId = resourceIDMapper.idFor(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 34d8c51d77..44e0a684b6 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 @@ -21,8 +21,8 @@ import java.util.concurrent.ConcurrentHashMap; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; 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.ResourceEventAware; @@ -35,8 +35,8 @@ public class CachingInboundEventSource public CachingInboundEventSource( ResourceFetcher resourceFetcher, Class resourceClass, - CacheKeyMapper cacheKeyMapper) { - super(resourceClass, cacheKeyMapper); + ResourceIDMapper resourceIDMapper) { + super(resourceClass, resourceIDMapper); this.resourceFetcher = resourceFetcher; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java index 73546dfd4f..599647ff29 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java @@ -22,12 +22,12 @@ import java.util.function.Predicate; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; public record PerResourcePollingConfiguration( String name, ScheduledExecutorService executorService, - CacheKeyMapper cacheKeyMapper, + ResourceIDMapper resourceIDMapper, PerResourcePollingEventSource.ResourceFetcher resourceFetcher, Predicate

registerPredicate, Duration defaultPollingPeriod) { @@ -37,7 +37,7 @@ public record PerResourcePollingConfiguration( public PerResourcePollingConfiguration( String name, ScheduledExecutorService executorService, - CacheKeyMapper cacheKeyMapper, + ResourceIDMapper resourceIDMapper, PerResourcePollingEventSource.ResourceFetcher resourceFetcher, Predicate

registerPredicate, Duration defaultPollingPeriod) { @@ -46,8 +46,8 @@ public PerResourcePollingConfiguration( executorService == null ? new ScheduledThreadPoolExecutor(DEFAULT_EXECUTOR_THREAD_NUMBER) : executorService; - this.cacheKeyMapper = - cacheKeyMapper == null ? CacheKeyMapper.resourceIdProviderMapper() : cacheKeyMapper; + this.resourceIDMapper = + resourceIDMapper == null ? ResourceIDMapper.resourceIdProviderMapper() : resourceIDMapper; this.resourceFetcher = Objects.requireNonNull(resourceFetcher); this.registerPredicate = registerPredicate; this.defaultPollingPeriod = defaultPollingPeriod; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfigurationBuilder.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfigurationBuilder.java index 7c15904742..6fcf5d2678 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfigurationBuilder.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfigurationBuilder.java @@ -20,7 +20,7 @@ import java.util.function.Predicate; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; public final class PerResourcePollingConfigurationBuilder { @@ -30,7 +30,7 @@ public final class PerResourcePollingConfigurationBuilder registerPredicate; private ScheduledExecutorService executorService; - private CacheKeyMapper cacheKeyMapper; + private ResourceIDMapper resourceIDMapper; public PerResourcePollingConfigurationBuilder( PerResourcePollingEventSource.ResourceFetcher resourceFetcher, @@ -52,9 +52,9 @@ public PerResourcePollingConfigurationBuilder withRegisterPredicate( return this; } - public PerResourcePollingConfigurationBuilder withCacheKeyMapper( - CacheKeyMapper cacheKeyMapper) { - this.cacheKeyMapper = cacheKeyMapper; + public PerResourcePollingConfigurationBuilder withResourceIDMapper( + ResourceIDMapper resourceIDMapper) { + this.resourceIDMapper = resourceIDMapper; return this; } @@ -67,7 +67,7 @@ public PerResourcePollingConfiguration build() { return new PerResourcePollingConfiguration<>( name, executorService, - cacheKeyMapper, + resourceIDMapper, resourceFetcher, registerPredicate, defaultPollingPeriod); 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 d0e8116cb4..0f0eb78a69 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 @@ -66,7 +66,7 @@ public PerResourcePollingEventSource( Class resourceClass, EventSourceContext

context, PerResourcePollingConfiguration config) { - super(config.name(), resourceClass, config.cacheKeyMapper()); + super(config.name(), resourceClass, config.resourceIDMapper()); this.primaryResourceCache = context.getPrimaryCache(); this.resourceFetcher = config.resourceFetcher(); this.registerPredicate = config.registerPredicate(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java index e48d4fd3e1..9ac1b8cc96 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java @@ -18,23 +18,23 @@ import java.time.Duration; import java.util.Objects; -import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; public record PollingConfiguration( String name, PollingEventSource.GenericResourceFetcher genericResourceFetcher, Duration period, - CacheKeyMapper cacheKeyMapper) { + ResourceIDMapper resourceIDMapper) { public PollingConfiguration( String name, PollingEventSource.GenericResourceFetcher genericResourceFetcher, Duration period, - CacheKeyMapper cacheKeyMapper) { + ResourceIDMapper resourceIDMapper) { this.name = name; this.genericResourceFetcher = Objects.requireNonNull(genericResourceFetcher); this.period = period; - this.cacheKeyMapper = - cacheKeyMapper == null ? CacheKeyMapper.resourceIdProviderMapper() : cacheKeyMapper; + this.resourceIDMapper = + resourceIDMapper == null ? ResourceIDMapper.resourceIdProviderMapper() : resourceIDMapper; } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java index 783dca99da..39c4ce4e54 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java @@ -17,12 +17,12 @@ import java.time.Duration; -import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; public final class PollingConfigurationBuilder { private final Duration period; private final PollingEventSource.GenericResourceFetcher genericResourceFetcher; - private CacheKeyMapper cacheKeyMapper; + private ResourceIDMapper resourceIDMapper; private String name; public PollingConfigurationBuilder( @@ -32,8 +32,8 @@ public PollingConfigurationBuilder( } public PollingConfigurationBuilder withCacheKeyMapper( - CacheKeyMapper cacheKeyMapper) { - this.cacheKeyMapper = cacheKeyMapper; + ResourceIDMapper resourceIDMapper) { + this.resourceIDMapper = resourceIDMapper; return this; } @@ -43,6 +43,6 @@ public PollingConfigurationBuilder withName(String name) { } public PollingConfiguration build() { - return new PollingConfiguration<>(name, genericResourceFetcher, period, cacheKeyMapper); + return new PollingConfiguration<>(name, genericResourceFetcher, period, resourceIDMapper); } } 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 be3b397343..f5e5a79430 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 @@ -68,7 +68,7 @@ public class PollingEventSource private final AtomicBoolean healthy = new AtomicBoolean(true); public PollingEventSource(Class resourceClass, PollingConfiguration config) { - super(config.name(), resourceClass, config.cacheKeyMapper()); + super(config.name(), resourceClass, config.resourceIDMapper()); this.genericResourceFetcher = config.genericResourceFetcher(); this.period = config.period(); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSourceTest.java index c02f6a288c..9356de626b 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSourceTest.java @@ -21,10 +21,10 @@ import org.junit.jupiter.api.Test; import io.javaoperatorsdk.operator.TestUtils; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; 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.CacheKeyMapper; import io.javaoperatorsdk.operator.processing.event.source.SampleExternalResource; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; @@ -48,7 +48,7 @@ class CachingInboundEventSourceTest supplier = mock(CachingInboundEventSource.ResourceFetcher.class); private final TestCustomResource testCustomResource = TestUtils.testCustomResource(); - private final CacheKeyMapper cacheKeyMapper = + private final ResourceIDMapper resourceIDMapper = r -> r.getName() + "#" + r.getValue(); @BeforeEach @@ -56,7 +56,7 @@ public void setup() { when(supplier.fetchResources(any())).thenReturn(Set.of(SampleExternalResource.testResource1())); setUpSource( - new CachingInboundEventSource<>(supplier, SampleExternalResource.class, cacheKeyMapper)); + new CachingInboundEventSource<>(supplier, SampleExternalResource.class, resourceIDMapper)); } @Test @@ -90,10 +90,10 @@ void propagateEventOnDeletedResource() throws InterruptedException { ResourceID.fromResource(testCustomResource), SampleExternalResource.testResource1()); source.handleResourceDeleteEvent( ResourceID.fromResource(testCustomResource), - cacheKeyMapper.keyFor(SampleExternalResource.testResource1())); + resourceIDMapper.idFor(SampleExternalResource.testResource1())); source.handleResourceDeleteEvent( ResourceID.fromResource(testCustomResource), - cacheKeyMapper.keyFor(SampleExternalResource.testResource2())); + resourceIDMapper.idFor(SampleExternalResource.testResource2())); verify(eventHandler, times(2)).handleEvent(any()); } 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 dbe93da915..907ed19fe5 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 @@ -25,9 +25,9 @@ import io.javaoperatorsdk.operator.TestUtils; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; import io.javaoperatorsdk.operator.processing.event.EventHandler; import io.javaoperatorsdk.operator.processing.event.source.AbstractEventSourceTestBase; -import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; import io.javaoperatorsdk.operator.processing.event.source.IndexerResourceCache; import io.javaoperatorsdk.operator.processing.event.source.SampleExternalResource; import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; @@ -70,7 +70,7 @@ public void setup() { new PerResourcePollingConfigurationBuilder< SampleExternalResource, TestCustomResource, String>( supplier, Duration.ofMillis(PERIOD)) - .withCacheKeyMapper(r -> r.getName() + "#" + r.getValue()) + .withResourceIDMapper(r -> r.getName() + "#" + r.getValue()) .build())); } @@ -99,7 +99,7 @@ void registeringTaskOnAPredicate() { supplier, Duration.ofMillis(PERIOD)) .withRegisterPredicate( testCustomResource -> testCustomResource.getMetadata().getGeneration() > 1) - .withCacheKeyMapper(CacheKeyMapper.resourceIdProviderMapper()) + .withResourceIDMapper(ResourceIDMapper.resourceIdProviderMapper()) .build())); source.onResourceCreated(testCustomResource); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java index 91aa487063..4c6eff7530 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java @@ -27,7 +27,7 @@ import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.api.reconciler.Reconciler; import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; -import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingConfigurationBuilder; import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingEventSource; @@ -67,7 +67,7 @@ public List> prepareEventSo return Set.of(UUID.randomUUID().toString()); }, Duration.ofMillis(POLL_PERIOD)) - .withCacheKeyMapper(CacheKeyMapper.singleResourceCacheKeyMapper()) + .withResourceIDMapper(ResourceIDMapper.singleResourceCacheKeyMapper()) .build()); return List.of(eventSource); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java index 41a4db2f02..569e974bff 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java @@ -45,6 +45,7 @@ public class BulkDependentResourceExternalWithState public BulkDependentResourceExternalWithState() { super(ExternalResource.class, Duration.ofMillis(300)); + setResourceIDMapper(this::externalResourceIndex); } @Override @@ -159,9 +160,4 @@ private String configMapName( ExternalStateBulkDependentCustomResource primary, ExternalResource resource) { return primary.getMetadata().getName() + DELIMITER + externalResourceIndex(resource); } - - @Override - public String keyFor(ExternalResource resource) { - return externalResourceIndex(resource); - } } From 232d7149d9b3daf0635c5dc88cec41ab7d2f04de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Thu, 23 Oct 2025 16:25:04 +0200 Subject: [PATCH 04/28] wip --- .../javaoperatorsdk/operator/processing/ResourceIDMapper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java index ebad96116a..4dd34844fa 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java @@ -22,8 +22,8 @@ public interface ResourceIDMapper { ID idFor(R resource); /** - * Used if a polling event source handles only single secondary resource and the id is String. See - * also docs for: {@link ExternalResourceCachingEventSource} + * Can be used if a polling event source handles only single secondary resource and the id is + * String. See also docs for: {@link ExternalResourceCachingEventSource} * * @return static id mapper, all resources are mapped for same id. * @param secondary resource type From cd28c5fd6a46b5bcd9fdedc9a28b976a6017bcba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 24 Oct 2025 13:11:06 +0200 Subject: [PATCH 05/28] wip --- .../io/javaoperatorsdk/operator/processing/ResourceIDMapper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java index 4dd34844fa..775c99c1ad 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java @@ -17,6 +17,7 @@ import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; +/** Provides id for the target resource. */ public interface ResourceIDMapper { ID idFor(R resource); From 0baa6c70f481a38d9b34179d4fd2222862bec869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 24 Oct 2025 13:44:23 +0200 Subject: [PATCH 06/28] feat: ResourceIDMapper for external event sources, external dependents and bulk dependents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../operator/processing/ResourceIDMapper.java | 10 +++- .../dependent/AbstractDependentResource.java | 2 +- .../dependent/BulkDependentResource.java | 9 ++-- .../BulkDependentResourceReconciler.java | 16 +++---- .../dependent/CRUDBulkDependentResource.java | 4 +- .../CRUDKubernetesBulkDependentResource.java | 23 ++++++++++ .../KubernetesBulkDependentResource.java | 46 +++++++++++++++++++ .../KubernetesDependentResource.java | 2 +- ...ConfigMapDeleterBulkDependentResource.java | 18 ++++---- .../ExternalBulkDependentResource.java | 2 +- .../ReadOnlyBulkDependentResource.java | 18 ++------ .../ReadOnlyBulkReadyPostCondition.java | 2 +- ...ulkDependentResourceExternalWithState.java | 6 ++- 13 files changed, 114 insertions(+), 44 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDKubernetesBulkDependentResource.java create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/KubernetesBulkDependentResource.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java index 775c99c1ad..ed5ca19485 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java @@ -15,6 +15,8 @@ */ package io.javaoperatorsdk.operator.processing; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; /** Provides id for the target resource. */ @@ -29,11 +31,11 @@ public interface ResourceIDMapper { * @return static id mapper, all resources are mapped for same id. * @param secondary resource type */ - static ResourceIDMapper singleResourceCacheKeyMapper() { + static ResourceIDMapper singleResourceCacheKeyMapper() { return r -> "id"; } - static ResourceIDMapper resourceIdProviderMapper() { + static ResourceIDMapper resourceIdProviderMapper() { return r -> { if (r instanceof ResourceIDProvider resourceIDProvider) { return (ID) resourceIDProvider.resourceId(); @@ -43,4 +45,8 @@ static ResourceIDMapper resourceIdProviderMapper() { } }; } + + static ResourceIDMapper kubernetesResourceIdMapper() { + return ResourceID::fromResource; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java index 9e827e16de..a7c5ce9e2d 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java @@ -63,7 +63,7 @@ protected AbstractDependentResource(String name) { dependentResourceReconciler = this instanceof BulkDependentResource - ? new BulkDependentResourceReconciler<>((BulkDependentResource) this) + ? new BulkDependentResourceReconciler<>((BulkDependentResource) this) : new SingleDependentResourceReconciler<>(this); this.name = name == null ? DependentResource.defaultNameFor(this.getClass()) : name; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java index 89cd09d837..e13614df2e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java @@ -27,10 +27,11 @@ * implementing this interface will typically also implement one or more additional interfaces such * as {@link Creator}, {@link Updater}, {@link Deleter}. * + * @param type of the id to distinguish resource * @param the dependent resource type * @param

the primary resource type */ -public interface BulkDependentResource { +public interface BulkDependentResource { /** * Retrieves a map of desired secondary resources associated with the specified primary resource, @@ -42,7 +43,7 @@ public interface BulkDependentResource { * @return a Map associating desired secondary resources with the specified primary via arbitrary * identifiers */ - default Map desiredResources(P primary, Context

context) { + default Map desiredResources(P primary, Context

context) { throw new IllegalStateException( "Implement desiredResources in case a non read-only bulk dependent resource"); } @@ -57,7 +58,7 @@ default Map desiredResources(P primary, Context

context) { * @return a Map associating actual secondary resources with the specified primary via arbitrary * identifiers */ - Map getSecondaryResources(P primary, Context

context); + Map getSecondaryResources(P primary, Context

context); /** * Deletes the actual resource identified by the specified key if it's not in the set of desired @@ -69,7 +70,7 @@ default Map desiredResources(P primary, Context

context) { * @param key key of the resource * @param context actual context */ - void deleteTargetResource(P primary, R resource, String key, Context

context); + void deleteTargetResource(P primary, R resource, ID key, Context

context); /** * Determines whether the specified secondary resource matches the desired state with target index diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResourceReconciler.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResourceReconciler.java index c808f75ea4..5b3617c26c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResourceReconciler.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResourceReconciler.java @@ -29,19 +29,19 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.dependent.Matcher.Result; -class BulkDependentResourceReconciler +class BulkDependentResourceReconciler implements DependentResourceReconciler { - private final BulkDependentResource bulkDependentResource; + private final BulkDependentResource bulkDependentResource; - BulkDependentResourceReconciler(BulkDependentResource bulkDependentResource) { + BulkDependentResourceReconciler(BulkDependentResource bulkDependentResource) { this.bulkDependentResource = bulkDependentResource; } @Override public ReconcileResult reconcile(P primary, Context

context) { - Map actualResources = bulkDependentResource.getSecondaryResources(primary, context); + Map actualResources = bulkDependentResource.getSecondaryResources(primary, context); if (!(bulkDependentResource instanceof Creator) && !(bulkDependentResource instanceof Deleter) && !(bulkDependentResource instanceof Updater)) { @@ -73,7 +73,7 @@ public void delete(P primary, Context

context) { } private void deleteExtraResources( - Set expectedKeys, Map actualResources, P primary, Context

context) { + Set expectedKeys, Map actualResources, P primary, Context

context) { actualResources.forEach( (key, value) -> { if (!expectedKeys.contains(key)) { @@ -90,13 +90,13 @@ private void deleteExtraResources( * @param

*/ @Ignore - private static class BulkDependentResourceInstance + private static class BulkDependentResourceInstance extends AbstractDependentResource implements Creator, Deleter

, Updater { - private final BulkDependentResource bulkDependentResource; + private final BulkDependentResource bulkDependentResource; private final R desired; private BulkDependentResourceInstance( - BulkDependentResource bulkDependentResource, R desired) { + BulkDependentResource bulkDependentResource, R desired) { this.bulkDependentResource = bulkDependentResource; this.desired = desired; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDBulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDBulkDependentResource.java index 565fcd65f6..59545f63d2 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDBulkDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDBulkDependentResource.java @@ -18,5 +18,5 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; -public interface CRUDBulkDependentResource - extends BulkDependentResource, Creator, BulkUpdater, Deleter

{} +public interface CRUDBulkDependentResource + extends BulkDependentResource, Creator, BulkUpdater, Deleter

{} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDKubernetesBulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDKubernetesBulkDependentResource.java new file mode 100644 index 0000000000..be15faf645 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDKubernetesBulkDependentResource.java @@ -0,0 +1,23 @@ +/* + * Copyright Java Operator SDK Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.javaoperatorsdk.operator.processing.dependent; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; +import io.javaoperatorsdk.operator.processing.event.ResourceID; + +public interface CRUDKubernetesBulkDependentResource + extends BulkDependentResource, Creator, BulkUpdater, Deleter

{} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/KubernetesBulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/KubernetesBulkDependentResource.java new file mode 100644 index 0000000000..fb815d31a5 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/KubernetesBulkDependentResource.java @@ -0,0 +1,46 @@ +/* + * Copyright Java Operator SDK Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.javaoperatorsdk.operator.processing.dependent; + +import java.util.Map; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; +import io.javaoperatorsdk.operator.processing.event.ResourceID; + +public interface KubernetesBulkDependentResource + extends BulkDependentResource, DependentResource { + + @Override + default Map getSecondaryResources(P primary, Context

context) { + return context + .getSecondaryResourcesAsStream(resourceType()) + .filter(secondaryResourceFilter(primary, context)) + .collect( + Collectors.toMap( + cm -> ResourceIDMapper.kubernetesResourceIdMapper().idFor(cm), + Function.identity())); + } + + default Predicate secondaryResourceFilter(P primary, Context

context) { + return r -> true; + } +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java index efe25b9a43..05cddcade1 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java @@ -205,7 +205,7 @@ protected void handleDelete(P primary, R secondary, Context

context) { } @SuppressWarnings("unused") - public void deleteTargetResource(P primary, R resource, String key, Context

context) { + public void deleteTargetResource(P primary, R resource, ResourceID key, Context

context) { context.getClient().resource(resource).delete(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/ConfigMapDeleterBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/ConfigMapDeleterBulkDependentResource.java index b234523a60..939039a8f5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/ConfigMapDeleterBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/ConfigMapDeleterBulkDependentResource.java @@ -23,13 +23,15 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; import io.javaoperatorsdk.operator.processing.dependent.*; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.event.ResourceID; /** Not using CRUDKubernetesDependentResource so the delete functionality can be tested. */ public class ConfigMapDeleterBulkDependentResource extends KubernetesDependentResource - implements CRUDBulkDependentResource { + implements CRUDKubernetesBulkDependentResource { public static final String LABEL_KEY = "bulk"; public static final String LABEL_VALUE = "true"; @@ -37,13 +39,14 @@ public class ConfigMapDeleterBulkDependentResource public static final String INDEX_DELIMITER = "-"; @Override - public Map desiredResources( + public Map desiredResources( BulkDependentTestCustomResource primary, Context context) { var number = primary.getSpec().getNumberOfResources(); - Map res = new HashMap<>(); + Map res = new HashMap<>(); for (int i = 0; i < number; i++) { var key = Integer.toString(i); - res.put(key, desired(primary, key)); + var desired = desired(primary, key); + res.put(ResourceIDMapper.kubernetesResourceIdMapper().idFor(desired), desired); } return res; } @@ -62,15 +65,12 @@ public ConfigMap desired(BulkDependentTestCustomResource primary, String key) { } @Override - public Map getSecondaryResources( + public Map getSecondaryResources( BulkDependentTestCustomResource primary, Context context) { return context .getSecondaryResourcesAsStream(ConfigMap.class) .filter(cm -> getName(cm).startsWith(primary.getMetadata().getName())) - .collect( - Collectors.toMap( - cm -> getName(cm).substring(getName(cm).lastIndexOf(INDEX_DELIMITER) + 1), - Function.identity())); + .collect(Collectors.toMap(ResourceID::fromResource, Function.identity())); } private static String getName(ConfigMap cm) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java index dda70286d8..aeb096d298 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java @@ -32,7 +32,7 @@ public class ExternalBulkDependentResource extends PollingDependentResource - implements BulkDependentResource, + implements BulkDependentResource, Creator, Deleter, BulkUpdater { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentResource.java index 01f68f2c77..2d5b612ad8 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentResource.java @@ -15,15 +15,13 @@ */ package io.javaoperatorsdk.operator.dependent.bulkdependent.readonly; -import java.util.Map; import java.util.Set; -import java.util.function.Function; -import java.util.stream.Collectors; +import java.util.function.Predicate; import io.fabric8.kubernetes.api.model.ConfigMap; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.dependent.bulkdependent.BulkDependentTestCustomResource; -import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.KubernetesBulkDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; import io.javaoperatorsdk.operator.processing.event.ResourceID; @@ -33,21 +31,15 @@ @KubernetesDependent public class ReadOnlyBulkDependentResource extends KubernetesDependentResource - implements BulkDependentResource, + implements KubernetesBulkDependentResource, SecondaryToPrimaryMapper { public static final String INDEX_DELIMITER = "-"; @Override - public Map getSecondaryResources( + public Predicate secondaryResourceFilter( BulkDependentTestCustomResource primary, Context context) { - return context - .getSecondaryResourcesAsStream(ConfigMap.class) - .filter(cm -> getName(cm).startsWith(primary.getMetadata().getName())) - .collect( - Collectors.toMap( - cm -> getName(cm).substring(getName(cm).lastIndexOf(INDEX_DELIMITER) + 1), - Function.identity())); + return cm -> getName(cm).startsWith(primary.getMetadata().getName()); } private static String getName(ConfigMap cm) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkReadyPostCondition.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkReadyPostCondition.java index 110ec0d4f0..cc331e70f0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkReadyPostCondition.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkReadyPostCondition.java @@ -32,7 +32,7 @@ public boolean isMet( var minResourceNumber = primary.getSpec().getNumberOfResources(); @SuppressWarnings("unchecked") var secondaryResources = - ((BulkDependentResource) dependentResource) + ((BulkDependentResource) dependentResource) .getSecondaryResources(primary, context); return minResourceNumber <= secondaryResources.size(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java index 569e974bff..ba08f7fdfa 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java @@ -35,8 +35,10 @@ public class BulkDependentResourceExternalWithState extends PerResourcePollingDependentResource< ExternalResource, ExternalStateBulkDependentCustomResource, String> - implements BulkDependentResource, - CRUDBulkDependentResource, + implements BulkDependentResource< + ExternalResource, ExternalStateBulkDependentCustomResource, String>, + CRUDBulkDependentResource< + ExternalResource, ExternalStateBulkDependentCustomResource, String>, DependentResourceWithExplicitState< ExternalResource, ExternalStateBulkDependentCustomResource, ConfigMap> { From 9e64e331692f17d6438892d7a9e550551b26e6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 24 Oct 2025 13:57:54 +0200 Subject: [PATCH 07/28] wip --- .../operator/processing/ResourceIDMapper.java | 10 ++++++++-- .../KubernetesBulkDependentResource.java | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java index ed5ca19485..f10b69a0da 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java @@ -19,7 +19,13 @@ import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; -/** Provides id for the target resource. */ +/** + * Provides id for the target resource. This mapper is used across multiple component of the + * framework, like the {@link + * io.javaoperatorsdk.operator.processing.dependent.AbstractExternalDependentResource}, in {@link + * ExternalResourceCachingEventSource}, and {@link + * io.javaoperatorsdk.operator.processing.dependent.KubernetesBulkDependentResource}. + */ public interface ResourceIDMapper { ID idFor(R resource); @@ -29,7 +35,7 @@ public interface ResourceIDMapper { * String. See also docs for: {@link ExternalResourceCachingEventSource} * * @return static id mapper, all resources are mapped for same id. - * @param secondary resource type + * @param secondary resource type */ static ResourceIDMapper singleResourceCacheKeyMapper() { return r -> "id"; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/KubernetesBulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/KubernetesBulkDependentResource.java index fb815d31a5..24a6afcae1 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/KubernetesBulkDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/KubernetesBulkDependentResource.java @@ -26,9 +26,21 @@ import io.javaoperatorsdk.operator.processing.ResourceIDMapper; import io.javaoperatorsdk.operator.processing.event.ResourceID; +/** + * A narrowed interface for bulk dependent resources for Kubernetes resources, that assumes that the + * ID is a {@link ResourceID}. Note that you are not limited to this when dealing with Kubernetes + * resources you can still choose a different ID type and directly implement {@link + * BulkDependentResource}. + */ public interface KubernetesBulkDependentResource extends BulkDependentResource, DependentResource { + /** + * Since we can list all the related resources and by assuming the ID is type of {@link + * ResourceID} it is trivial to create the target map. The only issue is if there are other + * secondary resources of the target type which are not managed by this bulk dependent resources, + * for those it is enough to override secondaryResourceFilter method. + */ @Override default Map getSecondaryResources(P primary, Context

context) { return context @@ -40,6 +52,10 @@ default Map getSecondaryResources(P primary, Context

context) Function.identity())); } + /** + * Override if not all the secondary resources of target type are managed by the bulk dependent + * resource. + */ default Predicate secondaryResourceFilter(P primary, Context

context) { return r -> true; } From 3928dba6d7f8cde322e59b87067def380da3f365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 24 Oct 2025 14:07:03 +0200 Subject: [PATCH 08/28] wip --- .../javaoperatorsdk/operator/processing/ResourceIDMapper.java | 2 +- .../event/source/polling/PollingConfigurationBuilder.java | 2 +- .../PerResourcePollingEventSourceTestReconciler.java | 2 +- .../MultipleManagedExternalDependentResourceReconciler.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java index f10b69a0da..fac9237500 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java @@ -37,7 +37,7 @@ public interface ResourceIDMapper { * @return static id mapper, all resources are mapped for same id. * @param secondary resource type */ - static ResourceIDMapper singleResourceCacheKeyMapper() { + static ResourceIDMapper singleResourceResourceIDMapper() { return r -> "id"; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java index 39c4ce4e54..0e68876b60 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java @@ -31,7 +31,7 @@ public PollingConfigurationBuilder( this.period = period; } - public PollingConfigurationBuilder withCacheKeyMapper( + public PollingConfigurationBuilder withResourceIDMapper( ResourceIDMapper resourceIDMapper) { this.resourceIDMapper = resourceIDMapper; return this; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java index 4c6eff7530..1e66ceacd5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java @@ -67,7 +67,7 @@ public List> prepareEventSo return Set.of(UUID.randomUUID().toString()); }, Duration.ofMillis(POLL_PERIOD)) - .withResourceIDMapper(ResourceIDMapper.singleResourceCacheKeyMapper()) + .withResourceIDMapper(ResourceIDMapper.singleResourceResourceIDMapper()) .build()); return List.of(eventSource); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java index 66939a3c49..dec9422486 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java @@ -99,7 +99,7 @@ public int getNumberOfExecutions() { new PollingConfigurationBuilder( fetcher, Duration.ofMillis(1000L)) .withName(EVENT_SOURCE_NAME) - .withCacheKeyMapper(ExternalResource::getId) + .withResourceIDMapper(ExternalResource::getId) .build()); return List.of(pollingEventSource); From bbed5262644797e1333bb6eff8c4a917c6fbeb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 24 Oct 2025 14:18:54 +0200 Subject: [PATCH 09/28] migration guide skeleton MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../en/docs/migration/v5-2-migration.md | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 docs/content/en/docs/migration/v5-2-migration.md diff --git a/docs/content/en/docs/migration/v5-2-migration.md b/docs/content/en/docs/migration/v5-2-migration.md new file mode 100644 index 0000000000..f11842f0a0 --- /dev/null +++ b/docs/content/en/docs/migration/v5-2-migration.md @@ -0,0 +1,21 @@ +--- +title: Migrating from v5.1 to v5.2 +description: Migrating from v5.1 to v5.2 +--- + +Version 5.2 brings some breaking changes on some components, for those we provide +a migration guide. For all the new features see release notes. + +## Custom ID types and ResourceIDProvider across multiple components + +TODO + +See also: + - related issue: [link](https://github.com/operator-framework/java-operator-sdk/issues/2972) + - related pull requests: + - [2970](https://github.com/operator-framework/java-operator-sdk/pull/2970) + - [3020](https://github.com/operator-framework/java-operator-sdk/pull/3020) + + + + From 323cb49ec74aed9bea0f48904c4414646002511b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 24 Oct 2025 14:29:10 +0200 Subject: [PATCH 10/28] changed sample MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../operator/processing/ResourceIDProvider.java | 2 +- .../external/ExternalBulkDependentResource.java | 13 ++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java index dcab1be92a..5dff02e1a5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java @@ -19,7 +19,7 @@ * Provides the identifier for an object that represents resource. This ID is used to select target * external resource for a dependent resource from the resources returned by `{@link * io.javaoperatorsdk.operator.api.reconciler.Context#getSecondaryResources(Class)}`. But also for - * {@link ResourceIDMapper} for event sources in external resources + * {@link ResourceIDMapper} for event sources in external resources. * * @param */ diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java index aeb096d298..46c65163a5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java @@ -95,10 +95,10 @@ public Map desiredResources( Map res = new HashMap<>(); for (int i = 0; i < number; i++) { var key = Integer.toString(i); - res.put( - key, + var resource = new ExternalResource( - toExternalResourceId(primary, key), primary.getSpec().getAdditionalData())); + toExternalResourceId(primary, key), primary.getSpec().getAdditionalData()); + res.put(getResourceIDMapper().idFor(resource), resource); } return res; } @@ -116,12 +116,7 @@ public Map getSecondaryResources( + EXTERNAL_RESOURCE_NAME_DELIMITER + primary.getMetadata().getNamespace() + EXTERNAL_RESOURCE_NAME_DELIMITER)) - .collect( - Collectors.toMap( - r -> - r.getId() - .substring(r.getId().lastIndexOf(EXTERNAL_RESOURCE_NAME_DELIMITER) + 1), - r -> r)); + .collect(Collectors.toMap(r -> getResourceIDMapper().idFor(r), r -> r)); } @Override From fe1dacbeac7fda69b1578108d67df781b44e1321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 24 Oct 2025 14:40:04 +0200 Subject: [PATCH 11/28] wip --- .../processing/ResourceIDProvider.java | 6 +- .../CRUDExternalBulkDependentResource.java | 24 ++++++++ .../ExternalBulkDependentResource.java | 57 +++++++++++++++++++ .../ExternalBulkResourceReconciler.java | 2 +- ... SampleExternalBulkDependentResource.java} | 37 +++++------- 5 files changed, 99 insertions(+), 27 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDExternalBulkDependentResource.java create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ExternalBulkDependentResource.java rename operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/{ExternalBulkDependentResource.java => SampleExternalBulkDependentResource.java} (75%) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java index 5dff02e1a5..cd6bc7aeed 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java @@ -21,9 +21,9 @@ * io.javaoperatorsdk.operator.api.reconciler.Context#getSecondaryResources(Class)}`. But also for * {@link ResourceIDMapper} for event sources in external resources. * - * @param + * @param */ -public interface ResourceIDProvider { +public interface ResourceIDProvider { - T resourceId(); + ID resourceId(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDExternalBulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDExternalBulkDependentResource.java new file mode 100644 index 0000000000..f290bcbbbc --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDExternalBulkDependentResource.java @@ -0,0 +1,24 @@ +/* + * Copyright Java Operator SDK Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.javaoperatorsdk.operator.processing.dependent; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; +import io.javaoperatorsdk.operator.processing.ResourceIDProvider; + +public interface CRUDExternalBulkDependentResource< + R extends ResourceIDProvider, P extends HasMetadata, ID> + extends ExternalBulkDependentResource, Creator, BulkUpdater, Deleter

{} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ExternalBulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ExternalBulkDependentResource.java new file mode 100644 index 0000000000..2e89a885ee --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ExternalBulkDependentResource.java @@ -0,0 +1,57 @@ +/* + * Copyright Java Operator SDK Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.javaoperatorsdk.operator.processing.dependent; + +import java.util.Map; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; +import io.javaoperatorsdk.operator.processing.ResourceIDProvider; + +public interface ExternalBulkDependentResource< + R extends ResourceIDProvider, P extends HasMetadata, ID> + extends BulkDependentResource, DependentResource { + + /** + * Since we can list all the related resources and by assuming the resource extends {{@link + * ResourceIDProvider}} it is trivial to create the target map. The only issue is if there are + * other secondary resources of the target type which are not managed by this bulk dependent + * resources, for those it is enough to override secondaryResourceFilter method. + */ + @Override + default Map getSecondaryResources(P primary, Context

context) { + return context + .getSecondaryResourcesAsStream(resourceType()) + .filter(secondaryResourceFilter(primary, context)) + .collect( + Collectors.toMap( + cm -> ResourceIDMapper.resourceIdProviderMapper().idFor(cm), + Function.identity())); + } + + /** + * Override if not all the secondary resources of target type are managed by the bulk dependent + * resource. + */ + default Predicate secondaryResourceFilter(P primary, Context

context) { + return r -> true; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkResourceReconciler.java index 28d65ecd5a..6753ee51fd 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkResourceReconciler.java @@ -19,7 +19,7 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.dependent.bulkdependent.BulkDependentTestCustomResource; -@Workflow(dependents = @Dependent(type = ExternalBulkDependentResource.class)) +@Workflow(dependents = @Dependent(type = SampleExternalBulkDependentResource.class)) @ControllerConfiguration() public class ExternalBulkResourceReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/SampleExternalBulkDependentResource.java similarity index 75% rename from operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java rename to operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/SampleExternalBulkDependentResource.java index 46c65163a5..b179d2f4f3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/SampleExternalBulkDependentResource.java @@ -19,29 +19,24 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; +import java.util.function.Predicate; import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; import io.javaoperatorsdk.operator.dependent.bulkdependent.BulkDependentTestCustomResource; -import io.javaoperatorsdk.operator.processing.dependent.BulkDependentResource; -import io.javaoperatorsdk.operator.processing.dependent.BulkUpdater; -import io.javaoperatorsdk.operator.processing.dependent.Creator; +import io.javaoperatorsdk.operator.processing.dependent.CRUDExternalBulkDependentResource; import io.javaoperatorsdk.operator.processing.dependent.external.PollingDependentResource; import io.javaoperatorsdk.operator.processing.event.ResourceID; -public class ExternalBulkDependentResource +public class SampleExternalBulkDependentResource extends PollingDependentResource - implements BulkDependentResource, - Creator, - Deleter, - BulkUpdater { + implements CRUDExternalBulkDependentResource< + ExternalResource, BulkDependentTestCustomResource, String> { public static final String EXTERNAL_RESOURCE_NAME_DELIMITER = "#"; private final ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); - public ExternalBulkDependentResource() { + public SampleExternalBulkDependentResource() { super(ExternalResource.class, ExternalResource::getId); } @@ -104,19 +99,15 @@ public Map desiredResources( } @Override - public Map getSecondaryResources( + public Predicate secondaryResourceFilter( BulkDependentTestCustomResource primary, Context context) { - return context - .getSecondaryResourcesAsStream(resourceType()) - .filter( - r -> - r.getId() - .startsWith( - primary.getMetadata().getName() - + EXTERNAL_RESOURCE_NAME_DELIMITER - + primary.getMetadata().getNamespace() - + EXTERNAL_RESOURCE_NAME_DELIMITER)) - .collect(Collectors.toMap(r -> getResourceIDMapper().idFor(r), r -> r)); + return r -> + r.getId() + .startsWith( + primary.getMetadata().getName() + + EXTERNAL_RESOURCE_NAME_DELIMITER + + primary.getMetadata().getNamespace() + + EXTERNAL_RESOURCE_NAME_DELIMITER); } @Override From 3a5a36d1500f15cb7d0250cd94a208ec2044b83b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 24 Oct 2025 14:51:22 +0200 Subject: [PATCH 12/28] wip --- .../AbstractExternalDependentResource.java | 2 +- .../ExternalBulkDependentResource.java | 38 +++------------- .../KubernetesBulkDependentResource.java | 33 ++------------ ...ResourceIDMapperBulkDependentResource.java | 44 +++++++++++++++++++ .../PerResourcePollingDependentResource.java | 2 +- .../SampleExternalBulkDependentResource.java | 2 +- 6 files changed, 57 insertions(+), 64 deletions(-) create mode 100644 operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ResourceIDMapperBulkDependentResource.java diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java index 6d0d76e9c5..e601e937cf 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java @@ -146,7 +146,7 @@ protected Optional selectTargetSecondaryResource( return targetResources.isEmpty() ? Optional.empty() : Optional.of(targetResources.get(0)); } - public ResourceIDMapper getResourceIDMapper() { + public ResourceIDMapper resourceIDMapper() { return resourceIDMapper; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ExternalBulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ExternalBulkDependentResource.java index 2e89a885ee..4ac15980f7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ExternalBulkDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ExternalBulkDependentResource.java @@ -15,43 +15,19 @@ */ package io.javaoperatorsdk.operator.processing.dependent; -import java.util.Map; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.processing.ResourceIDMapper; import io.javaoperatorsdk.operator.processing.ResourceIDProvider; +/** + * Specialized interface for bulk dependent resources where resource implement {@link + * ResourceIDProvider}. + */ public interface ExternalBulkDependentResource< R extends ResourceIDProvider, P extends HasMetadata, ID> - extends BulkDependentResource, DependentResource { - - /** - * Since we can list all the related resources and by assuming the resource extends {{@link - * ResourceIDProvider}} it is trivial to create the target map. The only issue is if there are - * other secondary resources of the target type which are not managed by this bulk dependent - * resources, for those it is enough to override secondaryResourceFilter method. - */ - @Override - default Map getSecondaryResources(P primary, Context

context) { - return context - .getSecondaryResourcesAsStream(resourceType()) - .filter(secondaryResourceFilter(primary, context)) - .collect( - Collectors.toMap( - cm -> ResourceIDMapper.resourceIdProviderMapper().idFor(cm), - Function.identity())); - } + extends ResourceIDMapperBulkDependentResource { - /** - * Override if not all the secondary resources of target type are managed by the bulk dependent - * resource. - */ - default Predicate secondaryResourceFilter(P primary, Context

context) { - return r -> true; + default ResourceIDMapper resourceIDMapper() { + return ResourceIDMapper.resourceIdProviderMapper(); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/KubernetesBulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/KubernetesBulkDependentResource.java index 24a6afcae1..025c4c65ff 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/KubernetesBulkDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/KubernetesBulkDependentResource.java @@ -15,14 +15,7 @@ */ package io.javaoperatorsdk.operator.processing.dependent; -import java.util.Map; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Collectors; - import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.api.reconciler.Context; -import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; import io.javaoperatorsdk.operator.processing.ResourceIDMapper; import io.javaoperatorsdk.operator.processing.event.ResourceID; @@ -33,30 +26,10 @@ * BulkDependentResource}. */ public interface KubernetesBulkDependentResource - extends BulkDependentResource, DependentResource { + extends ResourceIDMapperBulkDependentResource { - /** - * Since we can list all the related resources and by assuming the ID is type of {@link - * ResourceID} it is trivial to create the target map. The only issue is if there are other - * secondary resources of the target type which are not managed by this bulk dependent resources, - * for those it is enough to override secondaryResourceFilter method. - */ @Override - default Map getSecondaryResources(P primary, Context

context) { - return context - .getSecondaryResourcesAsStream(resourceType()) - .filter(secondaryResourceFilter(primary, context)) - .collect( - Collectors.toMap( - cm -> ResourceIDMapper.kubernetesResourceIdMapper().idFor(cm), - Function.identity())); - } - - /** - * Override if not all the secondary resources of target type are managed by the bulk dependent - * resource. - */ - default Predicate secondaryResourceFilter(P primary, Context

context) { - return r -> true; + default ResourceIDMapper resourceIDMapper() { + return ResourceIDMapper.kubernetesResourceIdMapper(); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ResourceIDMapperBulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ResourceIDMapperBulkDependentResource.java new file mode 100644 index 0000000000..e215ec0104 --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ResourceIDMapperBulkDependentResource.java @@ -0,0 +1,44 @@ +/* + * Copyright Java Operator SDK Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.javaoperatorsdk.operator.processing.dependent; + +import java.util.Map; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; + +public interface ResourceIDMapperBulkDependentResource + extends BulkDependentResource, DependentResource { + + ResourceIDMapper resourceIDMapper(); + + @Override + default Map getSecondaryResources(P primary, Context

context) { + return context + .getSecondaryResourcesAsStream(resourceType()) + .filter(secondaryResourceFilter(primary, context)) + .collect(Collectors.toMap(cm -> resourceIDMapper().idFor(cm), Function.identity())); + } + + default Predicate secondaryResourceFilter(P primary, Context

context) { + return r -> true; + } +} 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 15ac34aa6e..34624432eb 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 @@ -47,7 +47,7 @@ protected ExternalResourceCachingEventSource createEventSource( resourceType(), context, new PerResourcePollingConfigurationBuilder(this, getPollingPeriod()) - .withResourceIDMapper(getResourceIDMapper()) + .withResourceIDMapper(resourceIDMapper()) .withName(name()) .build()); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/SampleExternalBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/SampleExternalBulkDependentResource.java index b179d2f4f3..ba8a4a205a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/SampleExternalBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/SampleExternalBulkDependentResource.java @@ -93,7 +93,7 @@ public Map desiredResources( var resource = new ExternalResource( toExternalResourceId(primary, key), primary.getSpec().getAdditionalData()); - res.put(getResourceIDMapper().idFor(resource), resource); + res.put(resourceIDMapper().idFor(resource), resource); } return res; } From 9a5f7242616b9c8909dd8d3ad12f45620ced9262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 24 Oct 2025 15:19:03 +0200 Subject: [PATCH 13/28] wip --- docs/content/fileList.txt | 0 .../operator/processing/ResourceIDProvider.java | 6 +++++- .../dependent/ResourceIDMapperBulkDependentResource.java | 8 ++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) delete mode 100644 docs/content/fileList.txt diff --git a/docs/content/fileList.txt b/docs/content/fileList.txt deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java index cd6bc7aeed..80672f822b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java @@ -19,7 +19,11 @@ * Provides the identifier for an object that represents resource. This ID is used to select target * external resource for a dependent resource from the resources returned by `{@link * io.javaoperatorsdk.operator.api.reconciler.Context#getSecondaryResources(Class)}`. But also for - * {@link ResourceIDMapper} for event sources in external resources. + * {@link ResourceIDMapper} for event sources in external resources. But also for bulk dependent + * resource see {@link + * io.javaoperatorsdk.operator.processing.dependent.ExternalBulkDependentResource} and external + * event sources, see {@link + * io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource} * * @param */ diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ResourceIDMapperBulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ResourceIDMapperBulkDependentResource.java index e215ec0104..7cdd861cf6 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ResourceIDMapperBulkDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ResourceIDMapperBulkDependentResource.java @@ -38,6 +38,14 @@ default Map getSecondaryResources(P primary, Context

context) { .collect(Collectors.toMap(cm -> resourceIDMapper().idFor(cm), Function.identity())); } + /** + * Override of not all the secondary resources of a certain type are related to the target + * secondary resource. + * + * @param primary resource + * @param context of reconciliation + * @return predicate to filter secondary resources which are related to the bulk dependent. + */ default Predicate secondaryResourceFilter(P primary, Context

context) { return r -> true; } From 53ef664967cd95d82f85fc0e20e158c42602c640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Mon, 27 Oct 2025 17:08:48 +0100 Subject: [PATCH 14/28] Update operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java Co-authored-by: Martin Stefanko --- .../javaoperatorsdk/operator/processing/ResourceIDMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java index fac9237500..33f0aef4cb 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java @@ -20,7 +20,7 @@ import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; /** - * Provides id for the target resource. This mapper is used across multiple component of the + * Provides id for the target resource. This mapper is used across multiple components of the * framework, like the {@link * io.javaoperatorsdk.operator.processing.dependent.AbstractExternalDependentResource}, in {@link * ExternalResourceCachingEventSource}, and {@link From 66a4b8e80a42db2f6c19a4bca042151bf0a55a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Mon, 27 Oct 2025 17:08:58 +0100 Subject: [PATCH 15/28] Update operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java Co-authored-by: Martin Stefanko --- .../javaoperatorsdk/operator/processing/ResourceIDMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java index 33f0aef4cb..9b5d3cfeb6 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java @@ -22,7 +22,7 @@ /** * Provides id for the target resource. This mapper is used across multiple components of the * framework, like the {@link - * io.javaoperatorsdk.operator.processing.dependent.AbstractExternalDependentResource}, in {@link + * io.javaoperatorsdk.operator.processing.dependent.AbstractExternalDependentResource}, {@link * ExternalResourceCachingEventSource}, and {@link * io.javaoperatorsdk.operator.processing.dependent.KubernetesBulkDependentResource}. */ From 89469f9a423eddbeecbbc31c710325ebdeb103ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Mon, 27 Oct 2025 17:09:06 +0100 Subject: [PATCH 16/28] Update operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java Co-authored-by: Martin Stefanko --- .../javaoperatorsdk/operator/processing/ResourceIDProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java index 80672f822b..82715d7bb1 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java @@ -16,7 +16,7 @@ package io.javaoperatorsdk.operator.processing; /** - * Provides the identifier for an object that represents resource. This ID is used to select target + * Provides the identifier for an object that represents a resource. This ID is used to select the target * external resource for a dependent resource from the resources returned by `{@link * io.javaoperatorsdk.operator.api.reconciler.Context#getSecondaryResources(Class)}`. But also for * {@link ResourceIDMapper} for event sources in external resources. But also for bulk dependent From f31dc0eda8ebeafc098012153cdd9954bb6648e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Mon, 27 Oct 2025 17:09:14 +0100 Subject: [PATCH 17/28] Update operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java Co-authored-by: Martin Stefanko --- .../javaoperatorsdk/operator/processing/ResourceIDProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java index 82715d7bb1..6f6db88a70 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java @@ -18,7 +18,7 @@ /** * Provides the identifier for an object that represents a resource. This ID is used to select the target * external resource for a dependent resource from the resources returned by `{@link - * io.javaoperatorsdk.operator.api.reconciler.Context#getSecondaryResources(Class)}`. But also for + * io.javaoperatorsdk.operator.api.reconciler.Context#getSecondaryResources(Class)}`. It is also used in * {@link ResourceIDMapper} for event sources in external resources. But also for bulk dependent * resource see {@link * io.javaoperatorsdk.operator.processing.dependent.ExternalBulkDependentResource} and external From bbc9b1f4b49e6d441ef4867ab224c7f0e3e44c0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Mon, 27 Oct 2025 17:29:33 +0100 Subject: [PATCH 18/28] format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../operator/processing/ResourceIDProvider.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java index 6f6db88a70..d07804e6cb 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java @@ -16,11 +16,11 @@ package io.javaoperatorsdk.operator.processing; /** - * Provides the identifier for an object that represents a resource. This ID is used to select the target - * external resource for a dependent resource from the resources returned by `{@link - * io.javaoperatorsdk.operator.api.reconciler.Context#getSecondaryResources(Class)}`. It is also used in - * {@link ResourceIDMapper} for event sources in external resources. But also for bulk dependent - * resource see {@link + * Provides the identifier for an object that represents a resource. This ID is used to select the + * target external resource for a dependent resource from the resources returned by `{@link + * io.javaoperatorsdk.operator.api.reconciler.Context#getSecondaryResources(Class)}`. It is also + * used in {@link ResourceIDMapper} for event sources in external resources. But also for bulk + * dependent resource see {@link * io.javaoperatorsdk.operator.processing.dependent.ExternalBulkDependentResource} and external * event sources, see {@link * io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource} From cfcbc5199e39d5114bddc21aa99e24082079bcce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Wed, 29 Oct 2025 09:54:40 +0100 Subject: [PATCH 19/28] javadoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../operator/processing/ResourceIDMapper.java | 16 +++++++++---- .../processing/ResourceIDProvider.java | 23 +++++++++++-------- .../ExternalResourceCachingEventSource.java | 3 ++- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java index 9b5d3cfeb6..13fa78ec5b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java @@ -21,13 +21,21 @@ /** * Provides id for the target resource. This mapper is used across multiple components of the - * framework, like the {@link - * io.javaoperatorsdk.operator.processing.dependent.AbstractExternalDependentResource}, {@link - * ExternalResourceCachingEventSource}, and {@link - * io.javaoperatorsdk.operator.processing.dependent.KubernetesBulkDependentResource}. + * framework, like: + * + *

    + *
  • {@link io.javaoperatorsdk.operator.processing.dependent.AbstractExternalDependentResource} + *
  • {@link ExternalResourceCachingEventSource} + *
  • {@link io.javaoperatorsdk.operator.processing.dependent.KubernetesBulkDependentResource} + *
+ * + * @see ResourceIDProvider */ public interface ResourceIDMapper { + /** + * @return id for the target resource. + */ ID idFor(R resource); /** diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java index d07804e6cb..b784f35744 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java @@ -16,18 +16,23 @@ package io.javaoperatorsdk.operator.processing; /** - * Provides the identifier for an object that represents a resource. This ID is used to select the - * target external resource for a dependent resource from the resources returned by `{@link - * io.javaoperatorsdk.operator.api.reconciler.Context#getSecondaryResources(Class)}`. It is also - * used in {@link ResourceIDMapper} for event sources in external resources. But also for bulk - * dependent resource see {@link - * io.javaoperatorsdk.operator.processing.dependent.ExternalBulkDependentResource} and external - * event sources, see {@link - * io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource} + * Provides the identifier for an object that represents a resource. This ID is used: * - * @param + *
    + *
  • to select the target external resource for a dependent resource from the resources returned + * by {@link io.javaoperatorsdk.operator.api.reconciler.Context#getSecondaryResources(Class)}, + *
  • used in {@link ResourceIDMapper} for event sources in external resources. But also for bulk + * dependent resource see {@link + * io.javaoperatorsdk.operator.processing.dependent.ExternalBulkDependentResource}, + *
  • and external event sources, see {@link + * io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource} + *
+ * + * @see ResourceIDMapper + * @param type of the id */ public interface ResourceIDProvider { + /** ID for the resource POJO that implement this interface. */ ID resourceId(); } 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 259d6e6f0b..de85b4aad4 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 @@ -44,7 +44,8 @@ *

There are two related concepts to understand: * *

    - *
  • CacheKeyMapper - maps/extracts a key used to reference the associated resource in the cache + *
  • ResourceIDMapper - 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 1e4b19b97b480a4faa62282549579645303c2dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Wed, 29 Oct 2025 10:22:38 +0100 Subject: [PATCH 20/28] migration guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../en/docs/migration/v5-2-migration.md | 29 +++++++++++++++++-- .../PerResourcePollingDependentResource.java | 19 ++++++++++-- .../external/PollingDependentResource.java | 17 +++++++---- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/docs/content/en/docs/migration/v5-2-migration.md b/docs/content/en/docs/migration/v5-2-migration.md index f11842f0a0..a5b41f5113 100644 --- a/docs/content/en/docs/migration/v5-2-migration.md +++ b/docs/content/en/docs/migration/v5-2-migration.md @@ -6,9 +6,32 @@ description: Migrating from v5.1 to v5.2 Version 5.2 brings some breaking changes on some components, for those we provide a migration guide. For all the new features see release notes. -## Custom ID types and ResourceIDProvider across multiple components - -TODO +## Custom ID types across multiple components using ResourceIDMapper and ResourceIDProvider + +Working with an id of a resource is needed across various component in the framework. +Until this version the components provided by the framework assumed that you can easily +convert an id of the resource into String representation. So for example +[BulkDependentResources](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java#L46) +worked with a `Map` of resources, where id was always type of String. + +Mainly because of the need to manage external dependent resources more elegantly +we introduced a cross-cutting concept: [`ResourceIDMapper`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java). +That gets the ID of a resource. This is used then across various components, see: + + - [`ExternalResourceCachingEventSource`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java#L66) + - [`ExternalBulkDependentResource`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ExternalBulkDependentResource.java) + - [`AbstractExternalDependentResource`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java#L39) + and it's subclasses. + +We also added [`ResourceIDProvider`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java) +that you can implement into you Pojo representing a resource. + +The easiest way to migrate to this new approach is to implement this interface for your (external) resource, +and set the ID type generics for the components above. The default implementation of the `ResourceIDMapper` +works with `ResourceIDProvider` see [related implementation](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java#L52). + +If you cannot implement `ResourceIDProvider` because for example the class that represents the external resource is generated and final, +you can always set a custom `ResourceIDMapper` on the components above. See also: - related issue: [link](https://github.com/operator-framework/java-operator-sdk/issues/2972) 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 34624432eb..6b9aaf9c1e 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 @@ -20,6 +20,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.api.reconciler.Ignore; +import io.javaoperatorsdk.operator.processing.ResourceIDMapper; import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingConfigurationBuilder; import io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingEventSource; @@ -29,14 +30,26 @@ public abstract class PerResourcePollingDependentResource implements PerResourcePollingEventSource.ResourceFetcher { - public PerResourcePollingDependentResource() {} + protected PerResourcePollingDependentResource() {} - public PerResourcePollingDependentResource(Class resourceType) { + protected PerResourcePollingDependentResource( + Class resourceType, ResourceIDMapper resourceIDMapper) { super(resourceType); + setResourceIDMapper(resourceIDMapper); } - public PerResourcePollingDependentResource(Class resourceType, Duration pollingPeriod) { + protected PerResourcePollingDependentResource(Class resourceType) { + super(resourceType); + } + + protected PerResourcePollingDependentResource(Class resourceType, Duration pollingPeriod) { + super(resourceType, pollingPeriod); + } + + protected PerResourcePollingDependentResource( + Class resourceType, Duration pollingPeriod, ResourceIDMapper resourceIDMapper) { super(resourceType, pollingPeriod); + setResourceIDMapper(resourceIDMapper); } @Override 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 1abeeb1ef2..894e359d57 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 @@ -30,17 +30,24 @@ public abstract class PollingDependentResource extends AbstractPollingDependentResource implements PollingEventSource.GenericResourceFetcher { - private final ResourceIDMapper resourceIDMapper; + protected PollingDependentResource(Class resourceType) { + super(resourceType); + } - public PollingDependentResource(Class resourceType, ResourceIDMapper resourceIDMapper) { + protected PollingDependentResource( + Class resourceType, ResourceIDMapper resourceIDMapper) { super(resourceType); - this.resourceIDMapper = resourceIDMapper; + setResourceIDMapper(resourceIDMapper); + } + + protected PollingDependentResource(Class resourceType, Duration pollingPeriod) { + super(resourceType, pollingPeriod); } - public PollingDependentResource( + protected PollingDependentResource( Class resourceType, Duration pollingPeriod, ResourceIDMapper resourceIDMapper) { super(resourceType, pollingPeriod); - this.resourceIDMapper = resourceIDMapper; + setResourceIDMapper(resourceIDMapper); } @Override From 7fdb2f4dfe259b4287948bda201cfe5d4b1ec7dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Wed, 29 Oct 2025 10:30:38 +0100 Subject: [PATCH 21/28] migration guide grammar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../en/docs/migration/v5-2-migration.md | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/content/en/docs/migration/v5-2-migration.md b/docs/content/en/docs/migration/v5-2-migration.md index a5b41f5113..7bc28b6df7 100644 --- a/docs/content/en/docs/migration/v5-2-migration.md +++ b/docs/content/en/docs/migration/v5-2-migration.md @@ -3,34 +3,34 @@ title: Migrating from v5.1 to v5.2 description: Migrating from v5.1 to v5.2 --- -Version 5.2 brings some breaking changes on some components, for those we provide -a migration guide. For all the new features see release notes. +Version 5.2 brings some breaking changes to certain components. This document provides +a migration guide for these changes. For all the new features, see the release notes. ## Custom ID types across multiple components using ResourceIDMapper and ResourceIDProvider -Working with an id of a resource is needed across various component in the framework. -Until this version the components provided by the framework assumed that you can easily -convert an id of the resource into String representation. So for example +Working with the id of a resource is needed across various components in the framework. +Until this version, the components provided by the framework assumed that you could easily +convert the id of a resource into a String representation. For example, [BulkDependentResources](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java#L46) -worked with a `Map` of resources, where id was always type of String. +worked with a `Map` of resources, where the id was always of type String. -Mainly because of the need to manage external dependent resources more elegantly -we introduced a cross-cutting concept: [`ResourceIDMapper`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java). -That gets the ID of a resource. This is used then across various components, see: +Mainly because of the need to manage external dependent resources more elegantly, +we introduced a cross-cutting concept: [`ResourceIDMapper`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java), +which gets the ID of a resource. This is used across various components, see: - [`ExternalResourceCachingEventSource`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java#L66) - [`ExternalBulkDependentResource`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/ExternalBulkDependentResource.java) - [`AbstractExternalDependentResource`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java#L39) - and it's subclasses. + and its subclasses. -We also added [`ResourceIDProvider`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java) -that you can implement into you Pojo representing a resource. +We also added [`ResourceIDProvider`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java), +which you can implement in your Pojo representing a resource. -The easiest way to migrate to this new approach is to implement this interface for your (external) resource, -and set the ID type generics for the components above. The default implementation of the `ResourceIDMapper` -works with `ResourceIDProvider` see [related implementation](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java#L52). +The easiest way to migrate to this new approach is to implement this interface for your (external) resource +and set the ID type generics for the components above. The default implementation of the `ResourceIDMapper` +works with `ResourceIDProvider` (see [related implementation](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java#L52)). -If you cannot implement `ResourceIDProvider` because for example the class that represents the external resource is generated and final, +If you cannot implement `ResourceIDProvider` because, for example, the class that represents the external resource is generated and final, you can always set a custom `ResourceIDMapper` on the components above. See also: From 8f90ad3b6f00ed2ee7cfb80ddd9af391a162b9d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Wed, 29 Oct 2025 10:37:03 +0100 Subject: [PATCH 22/28] docs update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../dependent-resources.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md b/docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md index a73d600609..c3de3bcded 100644 --- a/docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md +++ b/docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md @@ -369,8 +369,12 @@ or we can use a matcher based SSA in most of the cases if the resource is manage ### Selecting the target resource Unfortunately this is not true for external resources. So to make sure we are selecting -the target resources from an event source, we provide a [mechanism](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java#L114-L138) that helps with that logic. -Your POJO representing an external resource can implement [`ResourceIDProvider`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java) : +the target resources from an event source, we provide a [mechanism](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java#L133-L147) that helps with that logic. +[`ResourceIDMapper`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java) +maps the resource to and ID and the ID of desired and actual resource is checked for equality. + +Your POJO representing an external resource can implement [`ResourceIDProvider`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java). +The default `ResourceIDMapper` implementation works on top of resource which implement `ResourceIDProvider`: ```java @@ -380,8 +384,7 @@ public interface ResourceIDProvider { } ``` -That will provide an ID, what is used to check for equality for desired state and resources from event source caches. -Not that if some reason this mechanism does not suit for you, you can simply +Note that if those approaches does not work for your use case, you can simply override [`selectTargetSecondaryResource`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java) method. From 579d0c296de85022d23b498d7618e21dd88646f5 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Thu, 30 Oct 2025 17:17:48 +0100 Subject: [PATCH 23/28] fix: use namespaced constant to avoid potential conflicts Signed-off-by: Chris Laprun --- .../javaoperatorsdk/operator/processing/ResourceIDMapper.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java index 13fa78ec5b..04e76e04ba 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java @@ -46,9 +46,10 @@ public interface ResourceIDMapper { * @param secondary resource type */ static ResourceIDMapper singleResourceResourceIDMapper() { - return r -> "id"; + return r -> "josdk:mapper:id"; } + @SuppressWarnings({"rawtypes", "unchecked"}) static ResourceIDMapper resourceIdProviderMapper() { return r -> { if (r instanceof ResourceIDProvider resourceIDProvider) { From d8f4c310432387961c8d69cc14e77a6e8546a8a0 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Thu, 30 Oct 2025 18:37:17 +0100 Subject: [PATCH 24/28] fix: typo Co-authored-by: Martin Stefanko --- .../dependent-resource-and-workflows/dependent-resources.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md b/docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md index c3de3bcded..1981729dc0 100644 --- a/docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md +++ b/docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md @@ -374,7 +374,7 @@ the target resources from an event source, we provide a [mechanism](https://gith maps the resource to and ID and the ID of desired and actual resource is checked for equality. Your POJO representing an external resource can implement [`ResourceIDProvider`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java). -The default `ResourceIDMapper` implementation works on top of resource which implement `ResourceIDProvider`: +The default `ResourceIDMapper` implementation works on top of resource which implements the `ResourceIDProvider`: ```java From bb741e8b5e019e10242d2bc7da7e5efd0b6b770f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Thu, 30 Oct 2025 18:54:46 +0100 Subject: [PATCH 25/28] revert change of the id, MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit it is by definition single, so namespace scoping is out of the picture, also does not confuse this way with some hidden intention in the background Signed-off-by: Attila Mészáros --- .../javaoperatorsdk/operator/processing/ResourceIDMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java index 04e76e04ba..6cb344cacb 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java @@ -46,7 +46,7 @@ public interface ResourceIDMapper { * @param secondary resource type */ static ResourceIDMapper singleResourceResourceIDMapper() { - return r -> "josdk:mapper:id"; + return r -> "id"; } @SuppressWarnings({"rawtypes", "unchecked"}) From 352067e0b11ffd4979ab5e7a95c6b563a7c80e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Sat, 1 Nov 2025 01:07:25 +0100 Subject: [PATCH 26/28] improve javadoc for ResourceIDMapper.singleResourceResourceIDMapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../operator/processing/ResourceIDMapper.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java index 6cb344cacb..9486953c93 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java @@ -40,7 +40,10 @@ public interface ResourceIDMapper { /** * Can be used if a polling event source handles only single secondary resource and the id is - * String. See also docs for: {@link ExternalResourceCachingEventSource} + * String. See also docs for: {@link ExternalResourceCachingEventSource}; or in {@link + * io.javaoperatorsdk.operator.processing.dependent.AbstractExternalDependentResource} when there + * is always only one secondary resource for that dependent resource. By definition cannot be used + * for a BulkDependent resources. * * @return static id mapper, all resources are mapped for same id. * @param secondary resource type From dc394772548b045a69ea5d64841871c765cbb3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Mon, 10 Nov 2025 09:07:04 -0500 Subject: [PATCH 27/28] comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../io/javaoperatorsdk/operator/processing/ResourceIDMapper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java index 9486953c93..5e2324f2bd 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java @@ -49,6 +49,7 @@ public interface ResourceIDMapper { * @param secondary resource type */ static ResourceIDMapper singleResourceResourceIDMapper() { + // the result could be any string, by definition would work with any value return r -> "id"; } From 3b39577ba8bd19b1bc88ed4310db7da8e86f83fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20M=C3=A9sz=C3=A1ros?= Date: Fri, 14 Nov 2025 11:47:41 -0500 Subject: [PATCH 28/28] changes from CR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Mészáros --- .../javaoperatorsdk/operator/processing/ResourceIDMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java index 5e2324f2bd..cc78bcbb82 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java @@ -50,7 +50,7 @@ public interface ResourceIDMapper { */ static ResourceIDMapper singleResourceResourceIDMapper() { // the result could be any string, by definition would work with any value - return r -> "id"; + return r -> "irrelevant"; } @SuppressWarnings({"rawtypes", "unchecked"})