diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java
index 3ffc913c5e..d2298d1fdf 100644
--- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java
@@ -1,6 +1,7 @@
package io.javaoperatorsdk.operator.api.config;
import java.time.Duration;
+import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@@ -13,6 +14,8 @@
import io.fabric8.kubernetes.api.model.ConfigMap;
import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.api.model.Secret;
+import io.fabric8.kubernetes.api.model.apps.Deployment;
+import io.fabric8.kubernetes.api.model.apps.StatefulSet;
import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.ConfigBuilder;
import io.fabric8.kubernetes.client.CustomResource;
@@ -448,6 +451,25 @@ default boolean previousAnnotationForDependentResourcesEventFiltering() {
return true;
}
+ /**
+ * For dependent resources framework can add an annotation to filter our events that are results
+ * of changes made by the framework. There are, however, few resources that do not follow the K8S
+ * API convention that changes in metadata do not increase the "metadata.generation". For these
+ * resources, the generation is increased by adding the annotation and their controller increases
+ * the observedGeneration in the status. This results in a new event, that if not handled
+ * correctly with the resource matcher yet again results in an update and a previous version
+ * annotation change, thus results in an infinite loop.
+ *
+ *
As a workaround, we automatically skip adding previous annotation for those well-known
+ * resources. Note that if you are sure that the matcher works (most of the cases does) for your
+ * case, you can remove the resource from the blocklist.
+ *
+ * @return blocklist of resource classes where the previous version annotation won't be used.
+ */
+ default List> previousAnnotationUsageBlocklist() {
+ return List.of(Deployment.class, StatefulSet.class);
+ }
+
/**
* If the event logic should parse the resourceVersion to determine the ordering of dependent
* resource events. This is typically not needed.
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java
index f420be0fff..4430f616d9 100644
--- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java
@@ -1,6 +1,7 @@
package io.javaoperatorsdk.operator.api.config;
import java.time.Duration;
+import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@@ -40,6 +41,7 @@ public class ConfigurationServiceOverrider {
private Boolean parseResourceVersions;
private Boolean useSSAToPatchPrimaryResource;
private Boolean cloneSecondaryResourcesWhenGettingFromCache;
+ private List> previousAnnotationUsageBlocklist;
@SuppressWarnings("rawtypes")
private DependentResourceFactory dependentResourceFactory;
@@ -188,6 +190,12 @@ public ConfigurationServiceOverrider withCloneSecondaryResourcesWhenGettingFromC
return this;
}
+ public ConfigurationServiceOverrider previousAnnotationUsageBlocklist(
+ List> previousAnnotationUsageBlacklist) {
+ this.previousAnnotationUsageBlocklist = previousAnnotationUsageBlacklist;
+ return this;
+ }
+
public ConfigurationService build() {
return new BaseConfigurationService(original.getVersion(), cloner, client) {
@Override
@@ -328,6 +336,13 @@ public boolean cloneSecondaryResourcesWhenGettingFromCache() {
cloneSecondaryResourcesWhenGettingFromCache,
ConfigurationService::cloneSecondaryResourcesWhenGettingFromCache);
}
+
+ @Override
+ public List> previousAnnotationUsageBlocklist() {
+ return overriddenValueOrDefault(
+ previousAnnotationUsageBlocklist,
+ ConfigurationService::previousAnnotationUsageBlocklist);
+ }
};
}
}
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 382ac7525c..54bf29a721 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
@@ -40,6 +40,7 @@ public abstract class KubernetesDependentResource kubernetesDependentResourceConfig;
private volatile Boolean useSSA;
+ private volatile Boolean usePreviousAnnotationForEventFiltering;
public KubernetesDependentResource(Class resourceType) {
this(resourceType, null);
@@ -160,10 +161,19 @@ protected boolean useSSA(Context