Skip to content

Commit ad0dc0d

Browse files
csvirimetacosm
andcommitted
feat: explicit event filters for all event sources (#1294)
Co-authored-by: Chris Laprun <[email protected]>
1 parent 0fff067 commit ad0dc0d

File tree

54 files changed

+1325
-325
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1325
-325
lines changed

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java

+103-26
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
import java.util.List;
99
import java.util.Optional;
1010
import java.util.Set;
11+
import java.util.function.BiPredicate;
1112
import java.util.function.Function;
13+
import java.util.function.Predicate;
1214
import java.util.stream.Collectors;
1315

1416
import io.fabric8.kubernetes.api.model.HasMetadata;
@@ -27,19 +29,23 @@
2729
import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
2830
import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter;
2931
import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilters;
32+
import io.javaoperatorsdk.operator.processing.event.source.filter.VoidGenericFilter;
33+
import io.javaoperatorsdk.operator.processing.event.source.filter.VoidOnAddFilter;
34+
import io.javaoperatorsdk.operator.processing.event.source.filter.VoidOnDeleteFilter;
35+
import io.javaoperatorsdk.operator.processing.event.source.filter.VoidOnUpdateFilter;
3036

3137
import static io.javaoperatorsdk.operator.api.reconciler.Constants.DEFAULT_NAMESPACES_SET;
3238

3339
@SuppressWarnings("rawtypes")
34-
public class AnnotationControllerConfiguration<R extends HasMetadata>
35-
implements io.javaoperatorsdk.operator.api.config.ControllerConfiguration<R> {
40+
public class AnnotationControllerConfiguration<P extends HasMetadata>
41+
implements io.javaoperatorsdk.operator.api.config.ControllerConfiguration<P> {
3642

37-
protected final Reconciler<R> reconciler;
43+
protected final Reconciler<P> reconciler;
3844
private final ControllerConfiguration annotation;
3945
private List<DependentResourceSpec> specs;
40-
private Class<R> resourceClass;
46+
private Class<P> resourceClass;
4147

42-
public AnnotationControllerConfiguration(Reconciler<R> reconciler) {
48+
public AnnotationControllerConfiguration(Reconciler<P> reconciler) {
4349
this.reconciler = reconciler;
4450
this.annotation = reconciler.getClass().getAnnotation(ControllerConfiguration.class);
4551
if (annotation == null) {
@@ -84,10 +90,10 @@ public Set<String> getNamespaces() {
8490

8591
@Override
8692
@SuppressWarnings("unchecked")
87-
public Class<R> getResourceClass() {
93+
public Class<P> getResourceClass() {
8894
if (resourceClass == null) {
8995
resourceClass =
90-
(Class<R>) Utils.getFirstTypeArgumentFromSuperClassOrInterface(reconciler.getClass(),
96+
(Class<P>) Utils.getFirstTypeArgumentFromSuperClassOrInterface(reconciler.getClass(),
9197
Reconciler.class);
9298
}
9399
return resourceClass;
@@ -105,16 +111,16 @@ public String getAssociatedReconcilerClassName() {
105111

106112
@SuppressWarnings("unchecked")
107113
@Override
108-
public ResourceEventFilter<R> getEventFilter() {
109-
ResourceEventFilter<R> answer = null;
114+
public ResourceEventFilter<P> getEventFilter() {
115+
ResourceEventFilter<P> answer = null;
110116

111-
Class<ResourceEventFilter<R>>[] filterTypes =
112-
(Class<ResourceEventFilter<R>>[]) valueOrDefault(annotation,
117+
Class<ResourceEventFilter<P>>[] filterTypes =
118+
(Class<ResourceEventFilter<P>>[]) valueOrDefault(annotation,
113119
ControllerConfiguration::eventFilters, new Object[] {});
114120
if (filterTypes.length > 0) {
115121
for (var filterType : filterTypes) {
116122
try {
117-
ResourceEventFilter<R> filter = filterType.getConstructor().newInstance();
123+
ResourceEventFilter<P> filter = filterType.getConstructor().newInstance();
118124

119125
if (answer == null) {
120126
answer = filter;
@@ -144,17 +150,55 @@ public Optional<Duration> reconciliationMaxInterval() {
144150
}
145151
}
146152

147-
public static <T> T valueOrDefault(
148-
ControllerConfiguration controllerConfiguration,
149-
Function<ControllerConfiguration, T> mapper,
150-
T defaultValue) {
151-
if (controllerConfiguration == null) {
152-
return defaultValue;
153+
@Override
154+
@SuppressWarnings("unchecked")
155+
public Optional<Predicate<P>> onAddFilter() {
156+
return (Optional<Predicate<P>>) createFilter(annotation.onAddFilter(), FilterType.onAdd,
157+
annotation.getClass().getSimpleName());
158+
}
159+
160+
private enum FilterType {
161+
onAdd(VoidOnAddFilter.class), onUpdate(VoidOnUpdateFilter.class), onDelete(
162+
VoidOnDeleteFilter.class), generic(VoidGenericFilter.class);
163+
164+
final Class<?> defaultValue;
165+
166+
FilterType(Class<?> defaultValue) {
167+
this.defaultValue = defaultValue;
168+
}
169+
}
170+
171+
private <T> Optional<T> createFilter(Class<T> filter, FilterType filterType, String origin) {
172+
if (filterType.defaultValue.equals(filter)) {
173+
return Optional.empty();
153174
} else {
154-
return mapper.apply(controllerConfiguration);
175+
try {
176+
var instance = (T) filter.getDeclaredConstructor().newInstance();
177+
return Optional.of(instance);
178+
} catch (InstantiationException | IllegalAccessException | InvocationTargetException
179+
| NoSuchMethodException e) {
180+
throw new OperatorException(
181+
"Couldn't create " + filterType + " filter from " + filter.getName() + " class in "
182+
+ origin + " for reconciler " + getName(),
183+
e);
184+
}
155185
}
156186
}
157187

188+
@SuppressWarnings("unchecked")
189+
@Override
190+
public Optional<BiPredicate<P, P>> onUpdateFilter() {
191+
return (Optional<BiPredicate<P, P>>) createFilter(annotation.onUpdateFilter(),
192+
FilterType.onUpdate, annotation.getClass().getSimpleName());
193+
}
194+
195+
@SuppressWarnings("unchecked")
196+
@Override
197+
public Optional<Predicate<P>> genericFilter() {
198+
return (Optional<Predicate<P>>) createFilter(annotation.genericFilter(),
199+
FilterType.generic, annotation.getClass().getSimpleName());
200+
}
201+
158202
@SuppressWarnings({"rawtypes", "unchecked"})
159203
@Override
160204
public List<DependentResourceSpec> getDependentResources() {
@@ -223,26 +267,59 @@ private String getName(Dependent dependent, Class<? extends DependentResource> d
223267
return name;
224268
}
225269

270+
@SuppressWarnings("rawtypes")
226271
private Object createKubernetesResourceConfig(Class<? extends DependentResource> dependentType) {
272+
227273
Object config;
228274
final var kubeDependent = dependentType.getAnnotation(KubernetesDependent.class);
229275

230276
var namespaces = getNamespaces();
231277
var configuredNS = false;
232-
if (kubeDependent != null && !Arrays.equals(KubernetesDependent.DEFAULT_NAMESPACES,
233-
kubeDependent.namespaces())) {
234-
namespaces = Set.of(kubeDependent.namespaces());
235-
configuredNS = true;
236-
}
237-
238278
String labelSelector = null;
279+
Predicate<? extends HasMetadata> onAddFilter = null;
280+
BiPredicate<? extends HasMetadata, ? extends HasMetadata> onUpdateFilter = null;
281+
BiPredicate<? extends HasMetadata, Boolean> onDeleteFilter = null;
282+
Predicate<? extends HasMetadata> genericFilter = null;
239283
if (kubeDependent != null) {
284+
if (!Arrays.equals(KubernetesDependent.DEFAULT_NAMESPACES,
285+
kubeDependent.namespaces())) {
286+
namespaces = Set.of(kubeDependent.namespaces());
287+
configuredNS = true;
288+
}
289+
240290
final var fromAnnotation = kubeDependent.labelSelector();
241291
labelSelector = Constants.NO_VALUE_SET.equals(fromAnnotation) ? null : fromAnnotation;
292+
293+
final var kubeDependentName = KubernetesDependent.class.getSimpleName();
294+
onAddFilter = createFilter(kubeDependent.onAddFilter(), FilterType.onAdd, kubeDependentName)
295+
.orElse(null);
296+
onUpdateFilter =
297+
createFilter(kubeDependent.onUpdateFilter(), FilterType.onUpdate, kubeDependentName)
298+
.orElse(null);
299+
onDeleteFilter =
300+
createFilter(kubeDependent.onDeleteFilter(), FilterType.onDelete, kubeDependentName)
301+
.orElse(null);
302+
genericFilter =
303+
createFilter(kubeDependent.genericFilter(), FilterType.generic, kubeDependentName)
304+
.orElse(null);
242305
}
243306

244307
config =
245-
new KubernetesDependentResourceConfig(namespaces, labelSelector, configuredNS);
308+
new KubernetesDependentResourceConfig(namespaces, labelSelector, configuredNS, onAddFilter,
309+
onUpdateFilter, onDeleteFilter, genericFilter);
310+
246311
return config;
247312
}
313+
314+
public static <T> T valueOrDefault(
315+
ControllerConfiguration controllerConfiguration,
316+
Function<ControllerConfiguration, T> mapper,
317+
T defaultValue) {
318+
if (controllerConfiguration == null) {
319+
return defaultValue;
320+
} else {
321+
return mapper.apply(controllerConfiguration);
322+
}
323+
}
324+
248325
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java

+30-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
package io.javaoperatorsdk.operator.api.config;
22

33
import java.time.Duration;
4-
import java.util.*;
4+
import java.util.HashSet;
5+
import java.util.LinkedHashMap;
6+
import java.util.List;
7+
import java.util.Optional;
8+
import java.util.Set;
9+
import java.util.function.BiPredicate;
510
import java.util.function.Predicate;
611
import java.util.stream.Collectors;
712

@@ -27,6 +32,9 @@ public class ControllerConfigurationOverrider<R extends HasMetadata> {
2732
private final ControllerConfiguration<R> original;
2833
private Duration reconciliationMaxInterval;
2934
private final LinkedHashMap<String, DependentResourceSpec> namedDependentResourceSpecs;
35+
private Predicate<R> onAddFilter;
36+
private BiPredicate<R, R> onUpdateFilter;
37+
private Predicate<R> genericFilter;
3038

3139
private ControllerConfigurationOverrider(ControllerConfiguration<R> original) {
3240
finalizer = original.getFinalizerName();
@@ -39,6 +47,9 @@ private ControllerConfigurationOverrider(ControllerConfiguration<R> original) {
3947
// make the original specs modifiable
4048
final var dependentResources = original.getDependentResources();
4149
namedDependentResourceSpecs = new LinkedHashMap<>(dependentResources.size());
50+
this.onAddFilter = original.onAddFilter().orElse(null);
51+
this.onUpdateFilter = original.onUpdateFilter().orElse(null);
52+
this.genericFilter = original.genericFilter().orElse(null);
4253
dependentResources.forEach(drs -> namedDependentResourceSpecs.put(drs.getName(), drs));
4354
this.original = original;
4455
}
@@ -120,6 +131,21 @@ public ControllerConfigurationOverrider<R> withReconciliationMaxInterval(
120131
return this;
121132
}
122133

134+
public ControllerConfigurationOverrider<R> withOnAddFilter(Predicate<R> onAddFilter) {
135+
this.onAddFilter = onAddFilter;
136+
return this;
137+
}
138+
139+
public ControllerConfigurationOverrider<R> withOnUpdateFilter(BiPredicate<R, R> onUpdateFilter) {
140+
this.onUpdateFilter = onUpdateFilter;
141+
return this;
142+
}
143+
144+
public ControllerConfigurationOverrider<R> withGenericFilter(Predicate<R> genericFilter) {
145+
this.genericFilter = genericFilter;
146+
return this;
147+
}
148+
123149
public ControllerConfigurationOverrider<R> replacingNamedDependentResourceConfig(String name,
124150
Object dependentResourceConfig) {
125151

@@ -167,6 +193,9 @@ public ControllerConfiguration<R> build() {
167193
customResourcePredicate,
168194
original.getResourceClass(),
169195
reconciliationMaxInterval,
196+
onAddFilter,
197+
onUpdateFilter,
198+
genericFilter,
170199
newDependentSpecs);
171200
}
172201

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultControllerConfiguration.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import java.util.List;
66
import java.util.Optional;
77
import java.util.Set;
8+
import java.util.function.BiPredicate;
9+
import java.util.function.Predicate;
810

911
import io.fabric8.kubernetes.api.model.HasMetadata;
1012
import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec;
@@ -39,8 +41,11 @@ public DefaultControllerConfiguration(
3941
ResourceEventFilter<R> resourceEventFilter,
4042
Class<R> resourceClass,
4143
Duration reconciliationMaxInterval,
44+
Predicate<R> onAddFilter,
45+
BiPredicate<R, R> onUpdateFilter,
46+
Predicate<R> genericFilter,
4247
List<DependentResourceSpec> dependents) {
43-
super(labelSelector, resourceClass, namespaces);
48+
super(labelSelector, resourceClass, onAddFilter, onUpdateFilter, genericFilter, namespaces);
4449
this.associatedControllerClassName = associatedControllerClassName;
4550
this.name = name;
4651
this.crdName = crdName;

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultResourceConfiguration.java

+28-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package io.javaoperatorsdk.operator.api.config;
22

3+
import java.util.Optional;
34
import java.util.Set;
5+
import java.util.function.BiPredicate;
6+
import java.util.function.Predicate;
47

58
import io.fabric8.kubernetes.api.model.HasMetadata;
69

@@ -12,18 +15,26 @@ public class DefaultResourceConfiguration<R extends HasMetadata>
1215
private final String labelSelector;
1316
private final Set<String> namespaces;
1417
private final Class<R> resourceClass;
18+
private final Predicate<R> onAddFilter;
19+
private final BiPredicate<R, R> onUpdateFilter;
20+
private final Predicate<R> genericFilter;
1521

1622
public DefaultResourceConfiguration(String labelSelector, Class<R> resourceClass,
17-
String... namespaces) {
18-
this(labelSelector, resourceClass,
23+
Predicate<R> onAddFilter,
24+
BiPredicate<R, R> onUpdateFilter, Predicate<R> genericFilter, String... namespaces) {
25+
this(labelSelector, resourceClass, onAddFilter, onUpdateFilter, genericFilter,
1926
namespaces == null || namespaces.length == 0 ? DEFAULT_NAMESPACES_SET
2027
: Set.of(namespaces));
2128
}
2229

2330
public DefaultResourceConfiguration(String labelSelector, Class<R> resourceClass,
24-
Set<String> namespaces) {
31+
Predicate<R> onAddFilter,
32+
BiPredicate<R, R> onUpdateFilter, Predicate<R> genericFilter, Set<String> namespaces) {
2533
this.labelSelector = labelSelector;
2634
this.resourceClass = resourceClass;
35+
this.onAddFilter = onAddFilter;
36+
this.onUpdateFilter = onUpdateFilter;
37+
this.genericFilter = genericFilter;
2738
this.namespaces =
2839
namespaces == null || namespaces.isEmpty() ? DEFAULT_NAMESPACES_SET
2940
: namespaces;
@@ -48,4 +59,18 @@ public Set<String> getNamespaces() {
4859
public Class<R> getResourceClass() {
4960
return resourceClass;
5061
}
62+
63+
@Override
64+
public Optional<Predicate<R>> onAddFilter() {
65+
return Optional.ofNullable(onAddFilter);
66+
}
67+
68+
@Override
69+
public Optional<BiPredicate<R, R>> onUpdateFilter() {
70+
return Optional.ofNullable(onUpdateFilter);
71+
}
72+
73+
public Optional<Predicate<R>> genericFilter() {
74+
return Optional.ofNullable(genericFilter);
75+
}
5176
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceConfiguration.java

+15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package io.javaoperatorsdk.operator.api.config;
22

33
import java.util.Collections;
4+
import java.util.Optional;
45
import java.util.Set;
6+
import java.util.function.BiPredicate;
7+
import java.util.function.Predicate;
58

69
import io.fabric8.kubernetes.api.model.HasMetadata;
710
import io.javaoperatorsdk.operator.OperatorException;
@@ -17,6 +20,18 @@ default String getResourceTypeName() {
1720
return ReconcilerUtils.getResourceTypeName(getResourceClass());
1821
}
1922

23+
default Optional<Predicate<R>> onAddFilter() {
24+
return Optional.empty();
25+
}
26+
27+
default Optional<BiPredicate<R, R>> onUpdateFilter() {
28+
return Optional.empty();
29+
}
30+
31+
default Optional<Predicate<R>> genericFilter() {
32+
return Optional.empty();
33+
}
34+
2035
/**
2136
* Retrieves the label selector that is used to filter which resources are actually watched by the
2237
* associated event source. See the official documentation on the

0 commit comments

Comments
 (0)