From 9b331da07f4f7eba5de277a73c5b1613a231fdde 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/26] 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 cb532df2a0187dd4c737ae6b5db35375c0d98866 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/26] 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 d0867e420a4d2cf5c8ef4486dbabc366dc8a2594 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/26] 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 f615a86604060d257e4bd16e40b06b0aa7dbe682 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/26] 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 a620cbe321e7a626833a6899e5ee4924b31a7b1f 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/26] 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 c295061d7c92618c9de00a0e200d9ba722c75dd4 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/26] 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 4d274b4dd7fa2acdaece23a7c196dde5f4ba9384 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/26] 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 c31eab1e4d8e5face0979d57b12bad334265cc56 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/26] 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 9c7b6ad437dbc4a0e87a3eb2c68c8d02857bd859 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/26] 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 b39cafefadb0e833d784d872fa9dcc3936918ce8 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/26] 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 e511b1a15c451e4f98338c8bcad75d4088dc1eeb 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/26] 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 bae1e762f60f5f1136ee997838f7560add733372 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/26] 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 5020e7668fda7efc3a91abd28f3c3ce9aaa4c0f5 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/26] 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 5332039c8ce1d8e2e3e59d44f205bdb9defbe555 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/26] 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 f8ccb784adaceaddc6dc43cfb1bed5ef9b3525dc 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/26] 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 3b25831eda524067322f5733b08c461b48d8a3e5 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/26] 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 1bb99cd45afe832a0485fb232b917b65ccd41139 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/26] 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 35be84a27783b6b0a19b7c7dad8ea60f1ab3d296 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/26] 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 be272321bdda3d1c137c00cab67109aa9a0e9a84 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/26] 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 bdf08c4fa5b643b853e77a6af667e24ef51c5151 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/26] 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 261e741d0c6d7766c0a814c16f41cc5b3f043f80 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/26] 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 108cb820d21e0a08fff4e909d2566bc9095d1004 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/26] 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 70038a6540ea525fb278286c1bfa8163c0bbc45a Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Thu, 30 Oct 2025 17:17:48 +0100 Subject: [PATCH 23/26] 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 e5d0380a575142d7153710c0926fdd54cd0daa11 Mon Sep 17 00:00:00 2001 From: Chris Laprun Date: Thu, 30 Oct 2025 18:37:17 +0100 Subject: [PATCH 24/26] 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 1bf7b1777e8cb3baedbed640c8a6c30c8816dd43 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/26] 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 adeabcc22ff5efb12c27a4a55b49f6d07840c023 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/26] 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