Skip to content

Commit 332ecf6

Browse files
csvirimetacosm
andcommitted
feat: remove automatic observed generation handling (#2382)
Signed-off-by: Attila Mészáros <csviri@gmail.com> Signed-off-by: Chris Laprun <claprun@redhat.com> Co-authored-by: Chris Laprun <claprun@redhat.com> Signed-off-by: Attila Mészáros <csviri@gmail.com>
1 parent c85aa73 commit 332ecf6

File tree

25 files changed

+163
-249
lines changed

25 files changed

+163
-249
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ It makes it easy to implement best practices and patterns for an Operator. Featu
4040
* Handling dependent resources, related events, and caching.
4141
* Automatic Retries
4242
* Smart event scheduling
43-
* Handling Observed Generations automatically
4443
* Easy to use Error Handling
4544
* ... and everything that a batteries included framework needs
4645

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package {{groupId}};
22

3-
import io.javaoperatorsdk.operator.api.ObservedGenerationAwareStatus;
4-
5-
public class {{artifactClassId}}Status extends ObservedGenerationAwareStatus {
3+
public class {{artifactClassId}}Status {
64

75
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
package io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope;
22

3-
import io.javaoperatorsdk.operator.api.ObservedGenerationAwareStatus;
4-
5-
public class BoundedCacheTestStatus extends ObservedGenerationAwareStatus {
3+
public class BoundedCacheTestStatus {
64
}

docs/documentation/v5-0-migration.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,6 @@ permalink: /docs/v5-0-migration
4444
where it is demonstrated. Also, the related part of
4545
a [workaround](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/StatusPatchSSAMigrationIT.java#L110-L116).
4646

47-
Related automatic observed generation handling changes:
48-
Automated Observed Generation (see features in docs), is automatically handled for non-SSA, even if
49-
the status sub-resource is not instructed to be updated. This is not true for SSA, observed generation is updated
50-
only when patch status is instructed by `UpdateControl`.
51-
5247
6. `ManagedDependentResourceContext` has been renamed to `ManagedWorkflowAndDependentResourceContext` and is accessed
5348
via the accordingly renamed `managedWorkflowAndDependentResourceContext` method.
5449
7. `ResourceDiscriminator` was removed. In most of the cases you can just delete the discriminator, everything should
@@ -57,3 +52,9 @@ permalink: /docs/v5-0-migration
5752
8. `ConfigurationService.getTerminationTimeoutSeconds` and associated overriding mechanism have been removed,
5853
use `Operator.stop(Duration)` instead.
5954
9. `Operator.installShutdownHook()` has been removed, use `Operator.installShutdownHook(Duration)` instead
55+
10. Automated observed generation handling feature was removed (`ObservedGenerationAware` interface
56+
and `ObservedGenerationAwareStatus` class were deleted). Manually handling observed generation is fairly easy to do
57+
in your reconciler, however, it cannot be done automatically when using SSA. We therefore removed the feature since
58+
it would have been confusing to have a different behavior for SSA and non-SSA cases. For an example of how to do
59+
observed generation handling manually in your reconciler, see
60+
[this sample](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/manualobservedgeneration/ManualObservedGenerationReconciler.java).

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

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

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

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

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ default boolean parseResourceVersionsForEventFilteringAndCaching() {
455455
/**
456456
* {@link io.javaoperatorsdk.operator.api.reconciler.UpdateControl} patch resource or status can
457457
* either use simple patches or SSA. Setting this to {@code true}, controllers will use SSA for
458-
* adding finalizers, managing observed generation, patching resources and status.
458+
* adding finalizers, patching resources and status.
459459
*
460460
* @return {@code true} if Server-Side Apply (SSA) should be used when patching the primary
461461
* resources, {@code false} otherwise

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,12 @@
1010
import io.fabric8.kubernetes.api.model.KubernetesResourceList;
1111
import io.fabric8.kubernetes.api.model.Namespaced;
1212
import io.fabric8.kubernetes.api.model.ObjectMeta;
13-
import io.fabric8.kubernetes.client.CustomResource;
1413
import io.fabric8.kubernetes.client.KubernetesClientException;
1514
import io.fabric8.kubernetes.client.dsl.MixedOperation;
1615
import io.fabric8.kubernetes.client.dsl.Resource;
1716
import io.fabric8.kubernetes.client.dsl.base.PatchContext;
1817
import io.fabric8.kubernetes.client.dsl.base.PatchType;
1918
import io.javaoperatorsdk.operator.OperatorException;
20-
import io.javaoperatorsdk.operator.api.ObservedGenerationAware;
2119
import io.javaoperatorsdk.operator.api.config.Cloner;
2220
import io.javaoperatorsdk.operator.api.config.ControllerConfiguration;
2321
import io.javaoperatorsdk.operator.api.reconciler.BaseControl;
@@ -166,13 +164,8 @@ private PostExecutionControl<P> reconcileExecution(ExecutionScope<P> executionSc
166164
}
167165
}
168166

169-
// check if status also needs to be updated
170-
final var updateObservedGeneration = updateControl.isNoUpdate()
171-
? shouldUpdateObservedGenerationAutomatically(resourceForExecution)
172-
: shouldUpdateObservedGenerationAutomatically(updatedCustomResource);
173-
// if using SSA the observed generation is updated only if user instructs patching the status
174-
if (updateControl.isPatchStatus() || (updateObservedGeneration && !useSSA)) {
175-
updatedCustomResource = patchStatusGenerationAware(toUpdate, originalResource);
167+
if (updateControl.isPatchStatus()) {
168+
customResourceFacade.patchStatus(toUpdate, originalResource);
176169
}
177170
return createPostExecutionControl(updatedCustomResource, updateControl);
178171
}
@@ -202,9 +195,8 @@ public boolean isLastAttempt() {
202195

203196
P updatedResource = null;
204197
if (errorStatusUpdateControl.getResource().isPresent()) {
205-
updatedResource =
206-
patchStatusGenerationAware(errorStatusUpdateControl.getResource().orElseThrow(),
207-
originalResource);
198+
updatedResource = customResourceFacade
199+
.patchStatus(errorStatusUpdateControl.getResource().orElseThrow(), originalResource);
208200
}
209201
if (errorStatusUpdateControl.isNoRetry()) {
210202
PostExecutionControl<P> postExecutionControl;
@@ -230,38 +222,9 @@ private boolean isErrorStatusHandlerPresent() {
230222
}
231223

232224
private P patchStatusGenerationAware(P resource, P originalResource) {
233-
updateStatusObservedGenerationIfRequired(resource);
234225
return customResourceFacade.patchStatus(resource, originalResource);
235226
}
236227

237-
@SuppressWarnings("rawtypes")
238-
private boolean shouldUpdateObservedGenerationAutomatically(P resource) {
239-
if (configuration().isGenerationAware() && resource instanceof CustomResource<?, ?>) {
240-
var customResource = (CustomResource) resource;
241-
var status = customResource.getStatus();
242-
// Note that if status is null we won't update the observed generation.
243-
if (status instanceof ObservedGenerationAware) {
244-
var observedGen = ((ObservedGenerationAware) status).getObservedGeneration();
245-
var currentGen = resource.getMetadata().getGeneration();
246-
return !currentGen.equals(observedGen);
247-
}
248-
}
249-
return false;
250-
}
251-
252-
@SuppressWarnings("rawtypes")
253-
private void updateStatusObservedGenerationIfRequired(P resource) {
254-
if (configuration().isGenerationAware() && resource instanceof CustomResource<?, ?>) {
255-
var customResource = (CustomResource) resource;
256-
var status = customResource.getStatus();
257-
// Note that if status is null we won't update the observed generation.
258-
if (status instanceof ObservedGenerationAware) {
259-
((ObservedGenerationAware) status)
260-
.setObservedGeneration(resource.getMetadata().getGeneration());
261-
}
262-
}
263-
}
264-
265228
private PostExecutionControl<P> createPostExecutionControl(P updatedCustomResource,
266229
UpdateControl<P> updateControl) {
267230
PostExecutionControl<P> postExecutionControl;

operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcherTest.java

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -435,29 +435,6 @@ void reScheduleOnDeleteWithoutFinalizerRemoval() {
435435
.isEqualTo(1000L);
436436
}
437437

438-
@Test
439-
void setObservedGenerationForStatusIfNeeded() throws Exception {
440-
var observedGenResource = createObservedGenCustomResource();
441-
442-
Reconciler<ObservedGenCustomResource> reconciler = mock(Reconciler.class);
443-
ControllerConfiguration<ObservedGenCustomResource> config =
444-
MockControllerConfiguration.forResource(ObservedGenCustomResource.class);
445-
CustomResourceFacade<ObservedGenCustomResource> facade = mock(CustomResourceFacade.class);
446-
var dispatcher = init(observedGenResource, reconciler, config, facade, true);
447-
448-
when(config.isGenerationAware()).thenReturn(true);
449-
450-
when(reconciler.reconcile(any(), any()))
451-
.thenReturn(UpdateControl.patchStatus(observedGenResource));
452-
when(facade.patchStatus(eq(observedGenResource), any())).thenReturn(observedGenResource);
453-
454-
PostExecutionControl<ObservedGenCustomResource> control = dispatcher.handleExecution(
455-
executionScopeWithCREvent(observedGenResource));
456-
assertThat(control.getUpdatedCustomResource().orElseGet(() -> fail("Missing optional"))
457-
.getStatus().getObservedGeneration())
458-
.isEqualTo(1L);
459-
}
460-
461438
@Test
462439
void doesNotUpdatesObservedGenerationIfStatusIsNotPatchedWhenUsingSSA() throws Exception {
463440
var observedGenResource = createObservedGenCustomResource();
@@ -476,28 +453,6 @@ void doesNotUpdatesObservedGenerationIfStatusIsNotPatchedWhenUsingSSA() throws E
476453
assertThat(control.getUpdatedCustomResource()).isEmpty();
477454
}
478455

479-
@Test
480-
void patchObservedGenerationOnCustomResourcePatchIfNoSSA() throws Exception {
481-
var observedGenResource = createObservedGenCustomResource();
482-
483-
Reconciler<ObservedGenCustomResource> reconciler = mock(Reconciler.class);
484-
final var config = MockControllerConfiguration.forResource(ObservedGenCustomResource.class);
485-
CustomResourceFacade<ObservedGenCustomResource> facade = mock(CustomResourceFacade.class);
486-
when(config.isGenerationAware()).thenReturn(true);
487-
when(reconciler.reconcile(any(), any()))
488-
.thenReturn(UpdateControl.patchResource(observedGenResource));
489-
when(facade.patchResource(any(), any())).thenReturn(observedGenResource);
490-
when(facade.patchStatus(eq(observedGenResource), any())).thenReturn(observedGenResource);
491-
initConfigService(false);
492-
var dispatcher = init(observedGenResource, reconciler, config, facade, true);
493-
494-
PostExecutionControl<ObservedGenCustomResource> control = dispatcher.handleExecution(
495-
executionScopeWithCREvent(observedGenResource));
496-
assertThat(control.getUpdatedCustomResource().orElseGet(() -> fail("Missing optional"))
497-
.getStatus().getObservedGeneration())
498-
.isEqualTo(1L);
499-
}
500-
501456
@Test
502457
void doesNotPatchObservedGenerationOnCustomResourcePatch() throws Exception {
503458
var observedGenResource = createObservedGenCustomResource();
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package io.javaoperatorsdk.operator.sample.observedgeneration;
22

3-
import io.javaoperatorsdk.operator.api.ObservedGenerationAwareStatus;
4-
5-
public class ObservedGenStatus extends ObservedGenerationAwareStatus {
3+
public class ObservedGenStatus {
64

75
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package io.javaoperatorsdk.operator;
2+
3+
import org.junit.jupiter.api.Test;
4+
import org.junit.jupiter.api.extension.RegisterExtension;
5+
6+
import io.fabric8.kubernetes.api.model.ObjectMetaBuilder;
7+
import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension;
8+
import io.javaoperatorsdk.operator.sample.manualobservedgeneration.ManualObservedGenerationCustomResource;
9+
import io.javaoperatorsdk.operator.sample.manualobservedgeneration.ManualObservedGenerationReconciler;
10+
import io.javaoperatorsdk.operator.sample.manualobservedgeneration.ManualObservedGenerationSpec;
11+
12+
import static org.assertj.core.api.Assertions.assertThat;
13+
import static org.awaitility.Awaitility.await;
14+
15+
public class ManualObservedGenerationIT {
16+
17+
public static final String RESOURCE_NAME = "test1";
18+
@RegisterExtension
19+
LocallyRunOperatorExtension extension =
20+
LocallyRunOperatorExtension.builder().withReconciler(new ManualObservedGenerationReconciler())
21+
.build();
22+
23+
@Test
24+
void observedGenerationUpdated() {
25+
extension.create(testResource());
26+
27+
await().untilAsserted(() -> {
28+
var r = extension.get(ManualObservedGenerationCustomResource.class, RESOURCE_NAME);
29+
assertThat(r).isNotNull();
30+
assertThat(r.getStatus().getObservedGeneration()).isEqualTo(1);
31+
assertThat(r.getStatus().getObservedGeneration()).isEqualTo(r.getMetadata().getGeneration());
32+
});
33+
34+
var changed = testResource();
35+
changed.getSpec().setValue("changed value");
36+
extension.replace(changed);
37+
38+
await().untilAsserted(() -> {
39+
var r = extension.get(ManualObservedGenerationCustomResource.class, RESOURCE_NAME);
40+
assertThat(r.getStatus().getObservedGeneration()).isEqualTo(2);
41+
assertThat(r.getStatus().getObservedGeneration()).isEqualTo(r.getMetadata().getGeneration());
42+
});
43+
}
44+
45+
ManualObservedGenerationCustomResource testResource() {
46+
var res = new ManualObservedGenerationCustomResource();
47+
res.setMetadata(new ObjectMetaBuilder()
48+
.withName(RESOURCE_NAME)
49+
.build());
50+
res.setSpec(new ManualObservedGenerationSpec());
51+
res.getSpec().setValue("Initial Value");
52+
return res;
53+
}
54+
}

operator-framework/src/test/java/io/javaoperatorsdk/operator/ObservedGenerationHandlingIT.java

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

operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/createupdateeventfilter/CreateUpdateEventFilterTestCustomResource.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,7 @@
1111
@ShortNames("cue")
1212
public class CreateUpdateEventFilterTestCustomResource
1313
extends
14-
CustomResource<CreateUpdateEventFilterTestCustomResourceSpec, CreateUpdateEventFilterTestCustomResourceStatus>
14+
CustomResource<CreateUpdateEventFilterTestCustomResourceSpec, Void>
1515
implements Namespaced {
1616

17-
@Override
18-
protected CreateUpdateEventFilterTestCustomResourceStatus initStatus() {
19-
return new CreateUpdateEventFilterTestCustomResourceStatus();
20-
}
21-
2217
}
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package io.javaoperatorsdk.operator.sample.createupdateeventfilter;
22

3-
import io.javaoperatorsdk.operator.api.ObservedGenerationAwareStatus;
4-
5-
public class CreateUpdateEventFilterTestCustomResourceStatus extends ObservedGenerationAwareStatus {
3+
public class CreateUpdateEventFilterTestCustomResourceStatus {
64

75
}
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
package io.javaoperatorsdk.operator.sample.gracefulstop;
22

3-
import io.javaoperatorsdk.operator.api.ObservedGenerationAwareStatus;
3+
public class GracefulStopTestCustomResourceStatus {
44

5-
public class GracefulStopTestCustomResourceStatus extends ObservedGenerationAwareStatus {
5+
private long observedGeneration;
66

7+
public long getObservedGeneration() {
8+
return observedGeneration;
9+
}
10+
11+
public void setObservedGeneration(long observedGeneration) {
12+
this.observedGeneration = observedGeneration;
13+
}
714
}

operator-framework/src/test/java/io/javaoperatorsdk/operator/sample/gracefulstop/GracefulStopTestReconciler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public UpdateControl<GracefulStopTestCustomResource> reconcile(
2222

2323
numberOfExecutions.addAndGet(1);
2424
resource.setStatus(new GracefulStopTestCustomResourceStatus());
25+
resource.getStatus().setObservedGeneration(resource.getMetadata().getGeneration());
2526
Thread.sleep(RECONCILER_SLEEP);
2627

2728
return UpdateControl.patchStatus(resource);

0 commit comments

Comments
 (0)