diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java
index 88f6b41197..9f25b47f49 100644
--- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java
@@ -157,7 +157,8 @@ protected
ControllerConfiguration
configFor(Reconcile
Constants.NO_VALUE_SET),
null,
Utils.instantiate(annotation.itemStore(), ItemStore.class, context), dependentFieldManager,
- this);
+ this,
+ null);
ResourceEventFilter
answer = deprecatedEventFilter(annotation);
config.setEventFilter(answer != null ? answer : ResourceEventFilters.passthrough());
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java
index 6d13ec2750..88a28480bd 100644
--- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java
@@ -40,6 +40,7 @@ public class ControllerConfigurationOverrider {
private ItemStore itemStore;
private String name;
private String fieldManager;
+ private Long informerListLimit;
private ControllerConfigurationOverrider(ControllerConfiguration original) {
this.finalizer = original.getFinalizerName();
@@ -56,6 +57,7 @@ private ControllerConfigurationOverrider(ControllerConfiguration original) {
this.rateLimiter = original.getRateLimiter();
this.name = original.getName();
this.fieldManager = original.fieldManager();
+ this.informerListLimit = original.getInformerListLimit().orElse(null);
}
public ControllerConfigurationOverrider withFinalizer(String finalizer) {
@@ -176,6 +178,20 @@ public ControllerConfigurationOverrider withFieldManager(
return this;
}
+
+ /**
+ * Sets a max page size limit when starting the informer. This will result in pagination while
+ * populating the cache. This means that longer lists will take multiple requests to fetch. See
+ * {@link io.fabric8.kubernetes.client.dsl.Informable#withLimit(Long)} for more details.
+ *
+ * @param informerListLimit null (the default) results in no pagination
+ */
+ public ControllerConfigurationOverrider withInformerListLimit(
+ Long informerListLimit) {
+ this.informerListLimit = informerListLimit;
+ return this;
+ }
+
public ControllerConfigurationOverrider replacingNamedDependentResourceConfig(String name,
Object dependentResourceConfig) {
@@ -199,7 +215,7 @@ public ControllerConfiguration build() {
reconciliationMaxInterval, onAddFilter, onUpdateFilter, genericFilter,
original.getDependentResources(),
namespaces, finalizer, labelSelector, configurations, itemStore, fieldManager,
- original.getConfigurationService());
+ original.getConfigurationService(), informerListLimit);
overridden.setEventFilter(customResourcePredicate);
return overridden;
}
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultResourceConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultResourceConfiguration.java
index 821553b2c8..87d8a6cd80 100644
--- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultResourceConfiguration.java
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultResourceConfiguration.java
@@ -21,11 +21,12 @@ public class DefaultResourceConfiguration
private final String labelSelector;
private final Set namespaces;
private final ItemStore itemStore;
+ private final Long informerListLimit;
protected DefaultResourceConfiguration(Class resourceClass,
Set namespaces, String labelSelector, OnAddFilter super R> onAddFilter,
OnUpdateFilter super R> onUpdateFilter, GenericFilter super R> genericFilter,
- ItemStore itemStore) {
+ ItemStore itemStore, Long informerListLimit) {
this.resourceClass = resourceClass;
this.resourceTypeName = ReconcilerUtils.getResourceTypeName(resourceClass);
this.onAddFilter = onAddFilter;
@@ -35,6 +36,7 @@ protected DefaultResourceConfiguration(Class resourceClass,
this.namespaces = ResourceConfiguration.ensureValidNamespaces(namespaces);
this.labelSelector = ResourceConfiguration.ensureValidLabelSelector(labelSelector);
this.itemStore = itemStore;
+ this.informerListLimit = informerListLimit;
}
@Override
@@ -76,4 +78,9 @@ public Optional> genericFilter() {
public Optional> getItemStore() {
return Optional.ofNullable(itemStore);
}
+
+ @Override
+ public Optional getInformerListLimit() {
+ return Optional.ofNullable(informerListLimit);
+ }
}
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java
index fd48a3ec6b..307e75080f 100644
--- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java
@@ -46,7 +46,8 @@ public ResolvedControllerConfiguration(Class resourceClass, ControllerConfigu
other.getDependentResources(), other.getNamespaces(),
other.getFinalizerName(), other.getLabelSelector(), Collections.emptyMap(),
other.getItemStore().orElse(null), other.fieldManager(),
- other.getConfigurationService());
+ other.getConfigurationService(),
+ other.getInformerListLimit().orElse(null));
}
public static Duration getMaxReconciliationInterval(long interval, TimeUnit timeUnit) {
@@ -75,11 +76,11 @@ public ResolvedControllerConfiguration(Class
resourceClass, String name,
Set namespaces, String finalizer, String labelSelector,
Map configurations, ItemStore itemStore,
String fieldManager,
- ConfigurationService configurationService) {
+ ConfigurationService configurationService, Long informerListLimit) {
this(resourceClass, name, generationAware, associatedReconcilerClassName, retry, rateLimiter,
maxReconciliationInterval, onAddFilter, onUpdateFilter, genericFilter,
namespaces, finalizer, labelSelector, configurations, itemStore, fieldManager,
- configurationService);
+ configurationService, informerListLimit);
setDependentResources(dependentResources);
}
@@ -91,9 +92,9 @@ protected ResolvedControllerConfiguration(Class
resourceClass, String name,
Set namespaces, String finalizer, String labelSelector,
Map configurations, ItemStore itemStore,
String fieldManager,
- ConfigurationService configurationService) {
+ ConfigurationService configurationService, Long informerListLimit) {
super(resourceClass, namespaces, labelSelector, onAddFilter, onUpdateFilter, genericFilter,
- itemStore);
+ itemStore, informerListLimit);
this.configurationService = configurationService;
this.name = ControllerConfiguration.ensureValidName(name, associatedReconcilerClassName);
this.generationAware = generationAware;
@@ -112,7 +113,7 @@ protected ResolvedControllerConfiguration(Class
resourceClass, String name,
Class extends Reconciler> reconcilerClas, ConfigurationService configurationService) {
this(resourceClass, name, false, getAssociatedReconcilerClassName(reconcilerClas), null, null,
null, null, null, null, null,
- null, null, null, null, null, configurationService);
+ null, null, null, null, null, configurationService, null);
}
@Override
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java
index 34b05a4573..d94504f50f 100644
--- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java
@@ -145,4 +145,12 @@ default Set getEffectiveNamespaces(ConfigurationService configurationSer
default Optional> getItemStore() {
return Optional.empty();
}
+
+ /**
+ * The maximum amount of items to return for a single list call when starting an informer. If this
+ * is a not null it will result in paginating for the initial load of the informer cache.
+ */
+ default Optional getInformerListLimit() {
+ return Optional.empty();
+ }
}
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerConfiguration.java
index 3ca680cf7f..1f40677ee7 100644
--- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerConfiguration.java
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerConfiguration.java
@@ -40,9 +40,9 @@ protected DefaultInformerConfiguration(String labelSelector,
OnUpdateFilter super R> onUpdateFilter,
OnDeleteFilter super R> onDeleteFilter,
GenericFilter super R> genericFilter,
- ItemStore itemStore) {
+ ItemStore itemStore, Long informerListLimit) {
super(resourceClass, namespaces, labelSelector, onAddFilter, onUpdateFilter, genericFilter,
- itemStore);
+ itemStore, informerListLimit);
this.followControllerNamespaceChanges = followControllerNamespaceChanges;
this.primaryToSecondaryMapper = primaryToSecondaryMapper;
@@ -119,6 +119,7 @@ class InformerConfigurationBuilder {
private GenericFilter super R> genericFilter;
private boolean inheritControllerNamespacesOnChange = false;
private ItemStore itemStore;
+ private Long informerListLimit;
private InformerConfigurationBuilder(Class resourceClass) {
this.resourceClass = resourceClass;
@@ -226,12 +227,24 @@ public InformerConfigurationBuilder withItemStore(ItemStore itemStore) {
return this;
}
+ /**
+ * Sets a max page size limit when starting the informer. This will result in pagination while
+ * populating the cache. This means that longer lists will take multiple requests to fetch. See
+ * {@link io.fabric8.kubernetes.client.dsl.Informable#withLimit(Long)} for more details.
+ *
+ * @param informerListLimit null (the default) results in no pagination
+ */
+ public InformerConfigurationBuilder withInformerListLimit(Long informerListLimit) {
+ this.informerListLimit = informerListLimit;
+ return this;
+ }
+
public InformerConfiguration build() {
return new DefaultInformerConfiguration<>(labelSelector, resourceClass,
primaryToSecondaryMapper,
secondaryToPrimaryMapper,
namespaces, inheritControllerNamespacesOnChange, onAddFilter, onUpdateFilter,
- onDeleteFilter, genericFilter, itemStore);
+ onDeleteFilter, genericFilter, itemStore, informerListLimit);
}
}
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java
index a1b58b8c66..88f1e51942 100644
--- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java
@@ -128,7 +128,8 @@ private InformerWrapper createEventSourceForNamespace(String namespace) {
private InformerWrapper createEventSource(
FilterWatchListDeletable, Resource> filteredBySelectorClient,
ResourceEventHandler eventHandler, String namespaceIdentifier) {
- var informer = filteredBySelectorClient.runnableInformer(0);
+ var informer = configuration.getInformerListLimit().map(filteredBySelectorClient::withLimit)
+ .orElse(filteredBySelectorClient).runnableInformer(0);
configuration.getItemStore().ifPresent(informer::itemStore);
var source = new InformerWrapper<>(informer, configurationService, namespaceIdentifier);
source.addEventHandler(eventHandler);
diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/MockKubernetesClient.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/MockKubernetesClient.java
index 78e1b0c789..eebf7a72d4 100644
--- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/MockKubernetesClient.java
+++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/MockKubernetesClient.java
@@ -12,13 +12,7 @@
import io.fabric8.kubernetes.api.model.authorization.v1.SubjectRulesReviewStatus;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.fabric8.kubernetes.client.V1ApiextensionAPIGroupDSL;
-import io.fabric8.kubernetes.client.dsl.AnyNamespaceOperation;
-import io.fabric8.kubernetes.client.dsl.ApiextensionsAPIGroupDSL;
-import io.fabric8.kubernetes.client.dsl.FilterWatchListDeletable;
-import io.fabric8.kubernetes.client.dsl.MixedOperation;
-import io.fabric8.kubernetes.client.dsl.NamespaceableResource;
-import io.fabric8.kubernetes.client.dsl.NonNamespaceOperation;
-import io.fabric8.kubernetes.client.dsl.Resource;
+import io.fabric8.kubernetes.client.dsl.*;
import io.fabric8.kubernetes.client.extended.leaderelection.LeaderElectorBuilder;
import io.fabric8.kubernetes.client.informers.SharedIndexInformer;
import io.fabric8.kubernetes.client.informers.cache.Indexer;
@@ -76,8 +70,15 @@ public static KubernetesClient client(Class clazz,
}
doAnswer(invocation -> null).when(informer).stop();
Indexer mockIndexer = mock(Indexer.class);
+
when(informer.getIndexer()).thenReturn(mockIndexer);
+
when(filterable.runnableInformer(anyLong())).thenReturn(informer);
+
+ Informable informable = mock(Informable.class);
+ when(filterable.withLimit(anyLong())).thenReturn(informable);
+ when(informable.runnableInformer(anyLong())).thenReturn(informer);
+
when(client.resources(clazz)).thenReturn(resources);
when(client.leaderElector())
.thenReturn(new LeaderElectorBuilder(client, Executors.newSingleThreadExecutor()));
diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventFilterTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventFilterTest.java
index 003b623b5d..c8ec839b59 100644
--- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventFilterTest.java
+++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventFilterTest.java
@@ -145,7 +145,7 @@ public ControllerConfig(String finalizer, boolean generationAware,
null,
null,
null,
- null, null, null, finalizer, null, null, null, new BaseConfigurationService());
+ null, null, null, finalizer, null, null, null, new BaseConfigurationService(), null);
setEventFilter(eventFilter);
}
}
diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSourceTest.java
index 6ee3d19d6c..87ec3f9eba 100644
--- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSourceTest.java
+++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSourceTest.java
@@ -198,7 +198,8 @@ public TestConfiguration(boolean generationAware, OnAddFilter