Skip to content

Commit a1fb15d

Browse files
metacosmcsviri
authored andcommitted
refactor: clean-up WorkflowReconcileResult and Condition (#1388)
Should make it easier to handle Conditions and Filters.
1 parent 98d5b4d commit a1fb15d

File tree

21 files changed

+232
-235
lines changed

21 files changed

+232
-235
lines changed

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

Lines changed: 35 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
2323
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
2424
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
25-
import io.javaoperatorsdk.operator.api.reconciler.dependent.VoidCondition;
2625
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
2726
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource;
2827
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfig;
@@ -34,10 +33,6 @@
3433
import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter;
3534
import io.javaoperatorsdk.operator.processing.event.source.filter.OnDeleteFilter;
3635
import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter;
37-
import io.javaoperatorsdk.operator.processing.event.source.filter.VoidGenericFilter;
38-
import io.javaoperatorsdk.operator.processing.event.source.filter.VoidOnAddFilter;
39-
import io.javaoperatorsdk.operator.processing.event.source.filter.VoidOnDeleteFilter;
40-
import io.javaoperatorsdk.operator.processing.event.source.filter.VoidOnUpdateFilter;
4136
import io.javaoperatorsdk.operator.processing.retry.Retry;
4237

4338
import static io.javaoperatorsdk.operator.api.reconciler.Constants.DEFAULT_NAMESPACES_SET;
@@ -46,6 +41,10 @@
4641
public class AnnotationControllerConfiguration<P extends HasMetadata>
4742
implements io.javaoperatorsdk.operator.api.config.ControllerConfiguration<P> {
4843

44+
private static final String CONTROLLER_CONFIG_ANNOTATION =
45+
ControllerConfiguration.class.getSimpleName();
46+
private static final String KUBE_DEPENDENT_NAME = KubernetesDependent.class.getSimpleName();
47+
4948
protected final Reconciler<P> reconciler;
5049
private final ControllerConfiguration annotation;
5150
private List<DependentResourceSpec> specs;
@@ -153,18 +152,19 @@ public Optional<Duration> maxReconciliationInterval() {
153152
@Override
154153
public RateLimiter getRateLimiter() {
155154
final Class<? extends RateLimiter> rateLimiterClass = annotation.rateLimiter();
156-
return instantiateAndConfigureIfNeeded(rateLimiterClass, RateLimiter.class);
155+
return instantiateAndConfigureIfNeeded(rateLimiterClass, RateLimiter.class,
156+
CONTROLLER_CONFIG_ANNOTATION);
157157
}
158158

159159
@Override
160160
public Retry getRetry() {
161161
final Class<? extends Retry> retryClass = annotation.retry();
162-
return instantiateAndConfigureIfNeeded(retryClass, Retry.class);
162+
return instantiateAndConfigureIfNeeded(retryClass, Retry.class, CONTROLLER_CONFIG_ANNOTATION);
163163
}
164164

165165
@SuppressWarnings("unchecked")
166-
private <T> T instantiateAndConfigureIfNeeded(Class<? extends T> targetClass,
167-
Class<T> expectedType) {
166+
protected <T> T instantiateAndConfigureIfNeeded(Class<? extends T> targetClass,
167+
Class<T> expectedType, String context) {
168168
try {
169169
final Constructor<? extends T> constructor = targetClass.getDeclaredConstructor();
170170
constructor.setAccessible(true);
@@ -184,57 +184,39 @@ private <T> T instantiateAndConfigureIfNeeded(Class<? extends T> targetClass,
184184
| NoSuchMethodException e) {
185185
throw new OperatorException("Couldn't instantiate " + expectedType.getSimpleName() + " '"
186186
+ targetClass.getName() + "' for '" + getName()
187-
+ "' reconciler. You need to provide an accessible no-arg constructor.", e);
187+
+ "' reconciler in " + context
188+
+ ". You need to provide an accessible no-arg constructor.", e);
188189
}
189190
}
190191

191192
@Override
192193
@SuppressWarnings("unchecked")
193194
public Optional<OnAddFilter<P>> onAddFilter() {
194-
return (Optional<OnAddFilter<P>>) createFilter(annotation.onAddFilter(), FilterType.onAdd,
195-
annotation.getClass().getSimpleName());
196-
}
197-
198-
private enum FilterType {
199-
onAdd(VoidOnAddFilter.class), onUpdate(VoidOnUpdateFilter.class), onDelete(
200-
VoidOnDeleteFilter.class), generic(VoidGenericFilter.class);
201-
202-
final Class<?> defaultValue;
203-
204-
FilterType(Class<?> defaultValue) {
205-
this.defaultValue = defaultValue;
206-
}
195+
return (Optional<OnAddFilter<P>>) createFilter(annotation.onAddFilter(), OnAddFilter.class,
196+
CONTROLLER_CONFIG_ANNOTATION);
207197
}
208198

209-
private <T> Optional<T> createFilter(Class<T> filter, FilterType filterType, String origin) {
210-
if (filterType.defaultValue.equals(filter)) {
199+
protected <T> Optional<? extends T> createFilter(Class<? extends T> filter, Class<T> defaultValue,
200+
String origin) {
201+
if (defaultValue.equals(filter)) {
211202
return Optional.empty();
212203
} else {
213-
try {
214-
var instance = (T) filter.getDeclaredConstructor().newInstance();
215-
return Optional.of(instance);
216-
} catch (InstantiationException | IllegalAccessException | InvocationTargetException
217-
| NoSuchMethodException e) {
218-
throw new OperatorException(
219-
"Couldn't create " + filterType + " filter from " + filter.getName() + " class in "
220-
+ origin + " for reconciler " + getName(),
221-
e);
222-
}
204+
return Optional.of(instantiateAndConfigureIfNeeded(filter, defaultValue, origin));
223205
}
224206
}
225207

226208
@SuppressWarnings("unchecked")
227209
@Override
228210
public Optional<OnUpdateFilter<P>> onUpdateFilter() {
229211
return (Optional<OnUpdateFilter<P>>) createFilter(annotation.onUpdateFilter(),
230-
FilterType.onUpdate, annotation.getClass().getSimpleName());
212+
OnUpdateFilter.class, CONTROLLER_CONFIG_ANNOTATION);
231213
}
232214

233215
@SuppressWarnings("unchecked")
234216
@Override
235217
public Optional<GenericFilter<P>> genericFilter() {
236218
return (Optional<GenericFilter<P>>) createFilter(annotation.genericFilter(),
237-
FilterType.generic, annotation.getClass().getSimpleName());
219+
GenericFilter.class, CONTROLLER_CONFIG_ANNOTATION);
238220
}
239221

240222
@SuppressWarnings({"rawtypes", "unchecked"})
@@ -260,13 +242,14 @@ public List<DependentResourceSpec> getDependentResources() {
260242
var spec = specsMap.get(name);
261243
if (spec != null) {
262244
throw new IllegalArgumentException(
263-
"A DependentResource named: " + name + " already exists: " + spec);
245+
"A DependentResource named '" + name + "' already exists: " + spec);
264246
}
247+
final var context = "DependentResource of type '" + dependentType.getName() + "'";
265248
spec = new DependentResourceSpec(dependentType, config, name,
266249
Set.of(dependent.dependsOn()),
267-
instantiateConditionIfNotVoid(dependent.readyPostcondition()),
268-
instantiateConditionIfNotVoid(dependent.reconcilePrecondition()),
269-
instantiateConditionIfNotVoid(dependent.deletePostcondition()));
250+
instantiateConditionIfNotDefault(dependent.readyPostcondition(), context),
251+
instantiateConditionIfNotDefault(dependent.reconcilePrecondition(), context),
252+
instantiateConditionIfNotDefault(dependent.deletePostcondition(), context));
270253
specsMap.put(name, spec);
271254
}
272255

@@ -275,9 +258,10 @@ public List<DependentResourceSpec> getDependentResources() {
275258
return specs;
276259
}
277260

278-
private Condition<?, ?> instantiateConditionIfNotVoid(Class<? extends Condition> condition) {
279-
if (condition != VoidCondition.class) {
280-
return instantiateAndConfigureIfNeeded(condition, Condition.class);
261+
protected Condition<?, ?> instantiateConditionIfNotDefault(Class<? extends Condition> condition,
262+
String context) {
263+
if (condition != Condition.class) {
264+
return instantiateAndConfigureIfNeeded(condition, Condition.class, context);
281265
}
282266
return null;
283267
}
@@ -313,17 +297,19 @@ private Object createKubernetesResourceConfig(Class<? extends DependentResource>
313297
final var fromAnnotation = kubeDependent.labelSelector();
314298
labelSelector = Constants.NO_VALUE_SET.equals(fromAnnotation) ? null : fromAnnotation;
315299

316-
final var kubeDependentName = KubernetesDependent.class.getSimpleName();
317-
onAddFilter = createFilter(kubeDependent.onAddFilter(), FilterType.onAdd, kubeDependentName)
300+
301+
final var context =
302+
KUBE_DEPENDENT_NAME + " annotation on " + dependentType.getName() + " DependentResource";
303+
onAddFilter = createFilter(kubeDependent.onAddFilter(), OnAddFilter.class, context)
318304
.orElse(null);
319305
onUpdateFilter =
320-
createFilter(kubeDependent.onUpdateFilter(), FilterType.onUpdate, kubeDependentName)
306+
createFilter(kubeDependent.onUpdateFilter(), OnUpdateFilter.class, context)
321307
.orElse(null);
322308
onDeleteFilter =
323-
createFilter(kubeDependent.onDeleteFilter(), FilterType.onDelete, kubeDependentName)
309+
createFilter(kubeDependent.onDeleteFilter(), OnDeleteFilter.class, context)
324310
.orElse(null);
325311
genericFilter =
326-
createFilter(kubeDependent.genericFilter(), FilterType.generic, kubeDependentName)
312+
createFilter(kubeDependent.genericFilter(), GenericFilter.class, context)
327313
.orElse(null);
328314
}
329315

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,13 @@
66
import java.lang.annotation.RetentionPolicy;
77
import java.lang.annotation.Target;
88

9-
import io.fabric8.kubernetes.api.model.HasMetadata;
109
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
1110
import io.javaoperatorsdk.operator.processing.event.rate.LinearRateLimiter;
1211
import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter;
1312
import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter;
1413
import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter;
1514
import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter;
1615
import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter;
17-
import io.javaoperatorsdk.operator.processing.event.source.filter.VoidGenericFilter;
18-
import io.javaoperatorsdk.operator.processing.event.source.filter.VoidOnAddFilter;
19-
import io.javaoperatorsdk.operator.processing.event.source.filter.VoidOnUpdateFilter;
2016
import io.javaoperatorsdk.operator.processing.retry.GenericRetry;
2117
import io.javaoperatorsdk.operator.processing.retry.Retry;
2218

@@ -79,15 +75,15 @@
7975
/**
8076
* Filter of onAdd events of resources.
8177
**/
82-
Class<? extends OnAddFilter<? extends HasMetadata>> onAddFilter() default VoidOnAddFilter.class;
78+
Class<? extends OnAddFilter> onAddFilter() default OnAddFilter.class;
8379

8480
/** Filter of onUpdate events of resources. */
85-
Class<? extends OnUpdateFilter<? extends HasMetadata>> onUpdateFilter() default VoidOnUpdateFilter.class;
81+
Class<? extends OnUpdateFilter> onUpdateFilter() default OnUpdateFilter.class;
8682

8783
/**
8884
* Filter applied to all operations (add, update, delete). Used to ignore some resources.
8985
**/
90-
Class<? extends GenericFilter<? extends HasMetadata>> genericFilter() default VoidGenericFilter.class;
86+
Class<? extends GenericFilter> genericFilter() default GenericFilter.class;
9187

9288
/**
9389
* Optional configuration of the maximal interval the SDK will wait for a reconciliation request

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,57 @@
44

55
import static io.javaoperatorsdk.operator.api.reconciler.Constants.NO_VALUE_SET;
66

7+
/**
8+
* The annotation used to create managed {@link DependentResource} associated with a given
9+
* {@link io.javaoperatorsdk.operator.api.reconciler.Reconciler}
10+
*/
711
public @interface Dependent {
812

913
@SuppressWarnings("rawtypes")
1014
Class<? extends DependentResource> type();
1115

16+
/**
17+
* The name of this dependent. This is needed to be able to refer to it when creating dependencies
18+
* between dependent resources.
19+
*
20+
* @return the name if it has been set,
21+
* {@link io.javaoperatorsdk.operator.api.reconciler.Constants#NO_VALUE_SET} otherwise
22+
*/
1223
String name() default NO_VALUE_SET;
1324

14-
@SuppressWarnings("rawtypes")
15-
Class<? extends Condition> readyPostcondition() default VoidCondition.class;
16-
17-
@SuppressWarnings("rawtypes")
18-
Class<? extends Condition> reconcilePrecondition() default VoidCondition.class;
19-
20-
@SuppressWarnings("rawtypes")
21-
Class<? extends Condition> deletePostcondition() default VoidCondition.class;
22-
25+
/**
26+
* The condition (if it exists) that needs to become true before the workflow can further proceed.
27+
*
28+
* @return a {@link Condition} implementation, defaulting to the interface itself if no value is
29+
* set
30+
*/
31+
Class<? extends Condition> readyPostcondition() default Condition.class;
32+
33+
/**
34+
* The condition (if it exists) that needs to become true before the associated
35+
* {@link DependentResource} is reconciled. Note that if this condition is set and the condition
36+
* doesn't hold true, the associated secondary will be deleted.
37+
*
38+
* @return a {@link Condition} implementation, defaulting to the interface itself if no value is
39+
* set
40+
*/
41+
Class<? extends Condition> reconcilePrecondition() default Condition.class;
42+
43+
/**
44+
* The condition (if it exists) that needs to become true before further reconciliation of
45+
* dependents can proceed after the secondary resource associated with this dependent is supposed
46+
* to have been deleted.
47+
*
48+
* @return a {@link Condition} implementation, defaulting to the interface itself if no value is
49+
* set
50+
*/
51+
Class<? extends Condition> deletePostcondition() default Condition.class;
52+
53+
/**
54+
* The list of named dependents that need to be reconciled before this one can be.
55+
*
56+
* @return the list (possibly empty) of named dependents that need to be reconciled before this
57+
* one can be
58+
*/
2359
String[] dependsOn() default {};
2460
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/VoidCondition.java

Lines changed: 0 additions & 14 deletions
This file was deleted.

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
import java.lang.annotation.RetentionPolicy;
66
import java.lang.annotation.Target;
77

8-
import io.fabric8.kubernetes.api.model.HasMetadata;
98
import io.javaoperatorsdk.operator.api.reconciler.Constants;
10-
import io.javaoperatorsdk.operator.processing.event.source.filter.*;
9+
import io.javaoperatorsdk.operator.processing.event.source.filter.GenericFilter;
10+
import io.javaoperatorsdk.operator.processing.event.source.filter.OnAddFilter;
11+
import io.javaoperatorsdk.operator.processing.event.source.filter.OnDeleteFilter;
12+
import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter;
1113

1214
import static io.javaoperatorsdk.operator.api.reconciler.Constants.NO_VALUE_SET;
1315

@@ -35,11 +37,35 @@
3537
*/
3638
String labelSelector() default NO_VALUE_SET;
3739

38-
Class<? extends OnAddFilter<? extends HasMetadata>> onAddFilter() default VoidOnAddFilter.class;
40+
/**
41+
* Optional {@link OnAddFilter} to filter events sent to this KubernetesDependent
42+
*
43+
* @return the {@link OnAddFilter} filter implementation to use, defaulting to the interface
44+
* itself if no value is set
45+
*/
46+
Class<? extends OnAddFilter> onAddFilter() default OnAddFilter.class;
3947

40-
Class<? extends OnUpdateFilter<? extends HasMetadata>> onUpdateFilter() default VoidOnUpdateFilter.class;
48+
/**
49+
* Optional {@link OnUpdateFilter} to filter events sent to this KubernetesDependent
50+
*
51+
* @return the {@link OnUpdateFilter} filter implementation to use, defaulting to the interface
52+
* itself if no value is set
53+
*/
54+
Class<? extends OnUpdateFilter> onUpdateFilter() default OnUpdateFilter.class;
4155

42-
Class<? extends OnDeleteFilter<? extends HasMetadata>> onDeleteFilter() default VoidOnDeleteFilter.class;
56+
/**
57+
* Optional {@link OnDeleteFilter} to filter events sent to this KubernetesDependent
58+
*
59+
* @return the {@link OnDeleteFilter} filter implementation to use, defaulting to the interface
60+
* itself if no value is set
61+
*/
62+
Class<? extends OnDeleteFilter> onDeleteFilter() default OnDeleteFilter.class;
4363

44-
Class<? extends GenericFilter<? extends HasMetadata>> genericFilter() default VoidGenericFilter.class;
64+
/**
65+
* Optional {@link GenericFilter} to filter events sent to this KubernetesDependent
66+
*
67+
* @return the {@link GenericFilter} filter implementation to use, defaulting to the interface
68+
* itself if no value is set
69+
*/
70+
Class<? extends GenericFilter> genericFilter() default GenericFilter.class;
4571
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/Condition.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,20 @@
22

33
import io.fabric8.kubernetes.api.model.HasMetadata;
44
import io.javaoperatorsdk.operator.api.reconciler.Context;
5-
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
65

76
public interface Condition<R, P extends HasMetadata> {
87

9-
boolean isMet(DependentResource<R, P> dependentResource, P primary, Context<P> context);
8+
/**
9+
* Checks whether a condition holds true for a given
10+
* {@link io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource} based on the
11+
* observed cluster state.
12+
*
13+
* @param primary the primary resource being considered
14+
* @param secondary the secondary resource associated with the specified primary resource or
15+
* {@code null} if no such secondary resource exists (for example because it's been
16+
* deleted)
17+
* @param context the current reconciliation {@link Context}
18+
* @return {@code true} if the condition holds, {@code false} otherwise
19+
*/
20+
boolean isMet(P primary, R secondary, Context<P> context);
1021
}

0 commit comments

Comments
 (0)