Skip to content

Commit f3ac194

Browse files
shawkinscsviri
authored andcommitted
Annotation removal using locking (#3015)
Signed-off-by: Steve Hawkins <[email protected]>
1 parent 9844fca commit f3ac194

File tree

15 files changed

+327
-444
lines changed

15 files changed

+327
-444
lines changed

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

Lines changed: 9 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
import io.fabric8.kubernetes.api.model.ConfigMap;
2929
import io.fabric8.kubernetes.api.model.HasMetadata;
3030
import io.fabric8.kubernetes.api.model.Secret;
31-
import io.fabric8.kubernetes.api.model.apps.Deployment;
32-
import io.fabric8.kubernetes.api.model.apps.StatefulSet;
3331
import io.fabric8.kubernetes.client.Config;
3432
import io.fabric8.kubernetes.client.ConfigBuilder;
3533
import io.fabric8.kubernetes.client.CustomResource;
@@ -46,6 +44,8 @@
4644
import io.javaoperatorsdk.operator.processing.dependent.workflow.ManagedWorkflowFactory;
4745
import io.javaoperatorsdk.operator.processing.event.source.controller.ControllerEventSource;
4846

47+
import static io.javaoperatorsdk.operator.api.reconciler.Constants.DEFAULT_COMPARABLE_RESOURCE_VERSIONS;
48+
4949
/** An interface from which to retrieve configuration information. */
5050
public interface ConfigurationService {
5151

@@ -448,61 +448,16 @@ default Set<Class<? extends HasMetadata>> defaultNonSSAResource() {
448448
}
449449

450450
/**
451-
* If a javaoperatorsdk.io/previous annotation should be used so that the operator sdk can detect
452-
* events from its own updates of dependent resources and then filter them.
451+
* If the event logic can compare resourceVersions.
453452
*
454-
* <p>Disable this if you want to react to your own dependent resource updates
453+
* <p>Enabled by default as Kubernetes does support this interpretation of resourceVersions.
454+
* Disable only if your api server provides non comparable resource versions.
455455
*
456-
* @return if special annotation should be used for dependent resource to filter events
457-
* @since 4.5.0
456+
* @return if resource versions are comparable
457+
* @since 5.3.0
458458
*/
459-
default boolean previousAnnotationForDependentResourcesEventFiltering() {
460-
return true;
461-
}
462-
463-
/**
464-
* For dependent resources, the framework can add an annotation to filter out events resulting
465-
* directly from the framework's operation. There are, however, some resources that do not follow
466-
* the Kubernetes API conventions that changes in metadata should not increase the generation of
467-
* the resource (as recorded in the {@code generation} field of the resource's {@code metadata}).
468-
* For these resources, this convention is not respected and results in a new event for the
469-
* framework to process. If that particular case is not handled correctly in the resource matcher,
470-
* the framework will consider that the resource doesn't match the desired state and therefore
471-
* triggers an update, which in turn, will re-add the annotation, thus starting the loop again,
472-
* infinitely.
473-
*
474-
* <p>As a workaround, we automatically skip adding previous annotation for those well-known
475-
* resources. Note that if you are sure that the matcher works for your use case, and it should in
476-
* most instances, you can remove the resource type from the blocklist.
477-
*
478-
* <p>The consequence of adding a resource type to the set is that the framework will not use
479-
* event filtering to prevent events, initiated by changes made by the framework itself as a
480-
* result of its processing of dependent resources, to trigger the associated reconciler again.
481-
*
482-
* <p>Note that this method only takes effect if annotating dependent resources to prevent
483-
* dependent resources events from triggering the associated reconciler again is activated as
484-
* controlled by {@link #previousAnnotationForDependentResourcesEventFiltering()}
485-
*
486-
* @return a Set of resource classes where the previous version annotation won't be used.
487-
*/
488-
default Set<Class<? extends HasMetadata>> withPreviousAnnotationForDependentResourcesBlocklist() {
489-
return Set.of(Deployment.class, StatefulSet.class);
490-
}
491-
492-
/**
493-
* If the event logic should parse the resourceVersion to determine the ordering of dependent
494-
* resource events. This is typically not needed.
495-
*
496-
* <p>Disabled by default as Kubernetes does not support, and discourages, this interpretation of
497-
* resourceVersions. Enable only if your api server event processing seems to lag the operator
498-
* logic, and you want to further minimize the amount of work done / updates issued by the
499-
* operator.
500-
*
501-
* @return if resource version should be parsed (as integer)
502-
* @since 4.5.0
503-
*/
504-
default boolean parseResourceVersionsForEventFilteringAndCaching() {
505-
return false;
459+
default boolean comparableResourceVersions() {
460+
return DEFAULT_COMPARABLE_RESOURCE_VERSIONS;
506461
}
507462

508463
/**

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

Lines changed: 8 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,9 @@ public class ConfigurationServiceOverrider {
5151
private Duration reconciliationTerminationTimeout;
5252
private Boolean ssaBasedCreateUpdateMatchForDependentResources;
5353
private Set<Class<? extends HasMetadata>> defaultNonSSAResource;
54-
private Boolean previousAnnotationForDependentResources;
55-
private Boolean parseResourceVersions;
54+
private Boolean comparableResourceVersions;
5655
private Boolean useSSAToPatchPrimaryResource;
5756
private Boolean cloneSecondaryResourcesWhenGettingFromCache;
58-
private Set<Class<? extends HasMetadata>> previousAnnotationUsageBlocklist;
5957

6058
@SuppressWarnings("rawtypes")
6159
private DependentResourceFactory dependentResourceFactory;
@@ -168,28 +166,23 @@ public ConfigurationServiceOverrider withDefaultNonSSAResource(
168166
return this;
169167
}
170168

171-
public ConfigurationServiceOverrider withPreviousAnnotationForDependentResources(boolean value) {
172-
this.previousAnnotationForDependentResources = value;
173-
return this;
174-
}
175-
176169
/**
177170
* @param value true if internal algorithms can use metadata.resourceVersion as a numeric value.
178171
* @return this
179172
*/
180-
public ConfigurationServiceOverrider withParseResourceVersions(boolean value) {
181-
this.parseResourceVersions = value;
173+
public ConfigurationServiceOverrider withComparableResourceVersions(boolean value) {
174+
this.comparableResourceVersions = value;
182175
return this;
183176
}
184177

185178
/**
186-
* @deprecated use withParseResourceVersions
179+
* @deprecated use withComparableResourceVersions
187180
* @param value true if internal algorithms can use metadata.resourceVersion as a numeric value.
188181
* @return this
189182
*/
190183
@Deprecated(forRemoval = true)
191-
public ConfigurationServiceOverrider wihtParseResourceVersions(boolean value) {
192-
this.parseResourceVersions = value;
184+
public ConfigurationServiceOverrider withParseResourceVersions(boolean value) {
185+
this.comparableResourceVersions = value;
193186
return this;
194187
}
195188

@@ -204,12 +197,6 @@ public ConfigurationServiceOverrider withCloneSecondaryResourcesWhenGettingFromC
204197
return this;
205198
}
206199

207-
public ConfigurationServiceOverrider withPreviousAnnotationForDependentResourcesBlocklist(
208-
Set<Class<? extends HasMetadata>> blocklist) {
209-
this.previousAnnotationUsageBlocklist = blocklist;
210-
return this;
211-
}
212-
213200
public ConfigurationService build() {
214201
return new BaseConfigurationService(original.getVersion(), cloner, client) {
215202
@Override
@@ -331,20 +318,6 @@ public Set<Class<? extends HasMetadata>> defaultNonSSAResources() {
331318
defaultNonSSAResource, ConfigurationService::defaultNonSSAResources);
332319
}
333320

334-
@Override
335-
public boolean previousAnnotationForDependentResourcesEventFiltering() {
336-
return overriddenValueOrDefault(
337-
previousAnnotationForDependentResources,
338-
ConfigurationService::previousAnnotationForDependentResourcesEventFiltering);
339-
}
340-
341-
@Override
342-
public boolean parseResourceVersionsForEventFilteringAndCaching() {
343-
return overriddenValueOrDefault(
344-
parseResourceVersions,
345-
ConfigurationService::parseResourceVersionsForEventFilteringAndCaching);
346-
}
347-
348321
@Override
349322
public boolean useSSAToPatchPrimaryResource() {
350323
return overriddenValueOrDefault(
@@ -359,11 +332,9 @@ public boolean cloneSecondaryResourcesWhenGettingFromCache() {
359332
}
360333

361334
@Override
362-
public Set<Class<? extends HasMetadata>>
363-
withPreviousAnnotationForDependentResourcesBlocklist() {
335+
public boolean comparableResourceVersions() {
364336
return overriddenValueOrDefault(
365-
previousAnnotationUsageBlocklist,
366-
ConfigurationService::withPreviousAnnotationForDependentResourcesBlocklist);
337+
comparableResourceVersions, ConfigurationService::comparableResourceVersions);
367338
}
368339
};
369340
}

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter;
3434
import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers;
3535

36+
import static io.javaoperatorsdk.operator.api.reconciler.Constants.DEFAULT_COMPARABLE_RESOURCE_VERSIONS;
3637
import static io.javaoperatorsdk.operator.api.reconciler.Constants.SAME_AS_CONTROLLER_NAMESPACES_SET;
3738
import static io.javaoperatorsdk.operator.api.reconciler.Constants.WATCH_ALL_NAMESPACE_SET;
3839
import static io.javaoperatorsdk.operator.api.reconciler.Constants.WATCH_CURRENT_NAMESPACE_SET;
@@ -96,18 +97,21 @@ class DefaultInformerEventSourceConfiguration<R extends HasMetadata>
9697
private final GroupVersionKind groupVersionKind;
9798
private final InformerConfiguration<R> informerConfig;
9899
private final KubernetesClient kubernetesClient;
100+
private final boolean comparableResourceVersions;
99101

100102
protected DefaultInformerEventSourceConfiguration(
101103
GroupVersionKind groupVersionKind,
102104
PrimaryToSecondaryMapper<?> primaryToSecondaryMapper,
103105
SecondaryToPrimaryMapper<R> secondaryToPrimaryMapper,
104106
InformerConfiguration<R> informerConfig,
105-
KubernetesClient kubernetesClient) {
107+
KubernetesClient kubernetesClient,
108+
boolean comparableResourceVersions) {
106109
this.informerConfig = Objects.requireNonNull(informerConfig);
107110
this.groupVersionKind = groupVersionKind;
108111
this.primaryToSecondaryMapper = primaryToSecondaryMapper;
109112
this.secondaryToPrimaryMapper = secondaryToPrimaryMapper;
110113
this.kubernetesClient = kubernetesClient;
114+
this.comparableResourceVersions = comparableResourceVersions;
111115
}
112116

113117
@Override
@@ -135,6 +139,11 @@ public Optional<GroupVersionKind> getGroupVersionKind() {
135139
public Optional<KubernetesClient> getKubernetesClient() {
136140
return Optional.ofNullable(kubernetesClient);
137141
}
142+
143+
@Override
144+
public boolean comparableResourceVersions() {
145+
return this.comparableResourceVersions;
146+
}
138147
}
139148

140149
@SuppressWarnings({"unused", "UnusedReturnValue"})
@@ -148,6 +157,7 @@ class Builder<R extends HasMetadata> {
148157
private PrimaryToSecondaryMapper<?> primaryToSecondaryMapper;
149158
private SecondaryToPrimaryMapper<R> secondaryToPrimaryMapper;
150159
private KubernetesClient kubernetesClient;
160+
private boolean comparableResourceVersions = DEFAULT_COMPARABLE_RESOURCE_VERSIONS;
151161

152162
private Builder(Class<R> resourceClass, Class<? extends HasMetadata> primaryResourceClass) {
153163
this(resourceClass, primaryResourceClass, null);
@@ -285,6 +295,11 @@ public Builder<R> withFieldSelector(FieldSelector fieldSelector) {
285295
return this;
286296
}
287297

298+
public Builder<R> withComparableResourceVersions(boolean comparableResourceVersions) {
299+
this.comparableResourceVersions = comparableResourceVersions;
300+
return this;
301+
}
302+
288303
public void updateFrom(InformerConfiguration<R> informerConfig) {
289304
if (informerConfig != null) {
290305
final var informerConfigName = informerConfig.getName();
@@ -324,7 +339,10 @@ public InformerEventSourceConfiguration<R> build() {
324339
HasMetadata.getKind(primaryResourceClass),
325340
false)),
326341
config.build(),
327-
kubernetesClient);
342+
kubernetesClient,
343+
comparableResourceVersions);
328344
}
329345
}
346+
347+
boolean comparableResourceVersions();
330348
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public final class Constants {
4141
public static final String RESOURCE_GVK_KEY = "josdk.resource.gvk";
4242
public static final String CONTROLLER_NAME = "controller.name";
4343
public static final boolean DEFAULT_FOLLOW_CONTROLLER_NAMESPACE_CHANGES = true;
44+
public static final boolean DEFAULT_COMPARABLE_RESOURCE_VERSIONS = true;
4445

4546
private Constants() {}
4647
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,11 @@ public static <P extends HasMetadata> P addFinalizerWithSSA(
451451
}
452452
}
453453

454+
public static int compareResourceVersions(HasMetadata h1, HasMetadata h2) {
455+
return compareResourceVersions(
456+
h1.getMetadata().getResourceVersion(), h2.getMetadata().getResourceVersion());
457+
}
458+
454459
public static int compareResourceVersions(String v1, String v2) {
455460
int v1Length = validateResourceVersion(v1);
456461
int v2Length = validateResourceVersion(v2);

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

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ public abstract class KubernetesDependentResource<R extends HasMetadata, P exten
5555
private final boolean garbageCollected = this instanceof GarbageCollected;
5656
private KubernetesDependentResourceConfig<R> kubernetesDependentResourceConfig;
5757
private volatile Boolean useSSA;
58-
private volatile Boolean usePreviousAnnotationForEventFiltering;
5958

6059
public KubernetesDependentResource() {}
6160

@@ -72,6 +71,27 @@ public void configureWith(KubernetesDependentResourceConfig<R> config) {
7271
this.kubernetesDependentResourceConfig = config;
7372
}
7473

74+
@Override
75+
protected R handleCreate(R desired, P primary, Context<P> context) {
76+
return eventSource()
77+
.orElseThrow()
78+
.updateAndCacheResource(
79+
desired,
80+
context,
81+
toCreate -> KubernetesDependentResource.super.handleCreate(toCreate, primary, context));
82+
}
83+
84+
@Override
85+
protected R handleUpdate(R actual, R desired, P primary, Context<P> context) {
86+
return eventSource()
87+
.orElseThrow()
88+
.updateAndCacheResource(
89+
desired,
90+
context,
91+
toUpdate ->
92+
KubernetesDependentResource.super.handleUpdate(actual, toUpdate, primary, context));
93+
}
94+
7595
@SuppressWarnings("unused")
7696
public R create(R desired, P primary, Context<P> context) {
7797
if (useSSA(context)) {
@@ -158,14 +178,6 @@ protected void addMetadata(
158178
} else {
159179
annotations.remove(InformerEventSource.PREVIOUS_ANNOTATION_KEY);
160180
}
161-
} else if (usePreviousAnnotation(context)) { // set a new one
162-
eventSource()
163-
.orElseThrow()
164-
.addPreviousAnnotation(
165-
Optional.ofNullable(actualResource)
166-
.map(r -> r.getMetadata().getResourceVersion())
167-
.orElse(null),
168-
target);
169181
}
170182
addReferenceHandlingMetadata(target, primary);
171183
}
@@ -181,22 +193,6 @@ protected boolean useSSA(Context<P> context) {
181193
return useSSA;
182194
}
183195

184-
private boolean usePreviousAnnotation(Context<P> context) {
185-
if (usePreviousAnnotationForEventFiltering == null) {
186-
usePreviousAnnotationForEventFiltering =
187-
context
188-
.getControllerConfiguration()
189-
.getConfigurationService()
190-
.previousAnnotationForDependentResourcesEventFiltering()
191-
&& !context
192-
.getControllerConfiguration()
193-
.getConfigurationService()
194-
.withPreviousAnnotationForDependentResourcesBlocklist()
195-
.contains(this.resourceType());
196-
}
197-
return usePreviousAnnotationForEventFiltering;
198-
}
199-
200196
@Override
201197
protected void handleDelete(P primary, R secondary, Context<P> context) {
202198
if (secondary != null) {

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerEventSource.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ public class ControllerEventSource<T extends HasMetadata>
4747

4848
@SuppressWarnings({"unchecked", "rawtypes"})
4949
public ControllerEventSource(Controller<T> controller) {
50-
super(NAME, controller.getCRClient(), controller.getConfiguration(), false);
50+
super(
51+
NAME,
52+
controller.getCRClient(),
53+
controller.getConfiguration(),
54+
controller.getConfiguration().getConfigurationService().comparableResourceVersions());
5155
this.controller = controller;
5256

5357
final var config = controller.getConfiguration();

0 commit comments

Comments
 (0)