Skip to content

Commit 609682e

Browse files
authored
Update observed gen on no-update (#697)
1 parent a047bb9 commit 609682e

File tree

4 files changed

+87
-44
lines changed

4 files changed

+87
-44
lines changed

Diff for: operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java

+12-8
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@
66
public class UpdateControl<T extends HasMetadata> extends BaseControl<UpdateControl<T>> {
77

88
private final T resource;
9-
private final boolean updateStatusSubResource;
9+
private final boolean updateStatus;
1010
private final boolean updateResource;
1111

1212
private UpdateControl(
13-
T resource, boolean updateStatusSubResource, boolean updateResource) {
14-
if ((updateResource || updateStatusSubResource) && resource == null) {
13+
T resource, boolean updateStatus, boolean updateResource) {
14+
if ((updateResource || updateStatus) && resource == null) {
1515
throw new IllegalArgumentException("CustomResource cannot be null in case of update");
1616
}
1717
this.resource = resource;
18-
this.updateStatusSubResource = updateStatusSubResource;
18+
this.updateStatus = updateStatus;
1919
this.updateResource = updateResource;
2020
}
2121

@@ -48,15 +48,19 @@ public T getResource() {
4848
return resource;
4949
}
5050

51-
public boolean isUpdateStatusSubResource() {
52-
return updateStatusSubResource;
51+
public boolean isUpdateStatus() {
52+
return updateStatus;
5353
}
5454

5555
public boolean isUpdateResource() {
5656
return updateResource;
5757
}
5858

59-
public boolean isUpdateCustomResourceAndStatusSubResource() {
60-
return updateResource && updateStatusSubResource;
59+
public boolean isNoUpdate() {
60+
return !updateResource && !updateStatus;
61+
}
62+
63+
public boolean isUpdateResourceAndStatus() {
64+
return updateResource && updateStatus;
6165
}
6266
}

Diff for: operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,10 @@ public String controllerName() {
8181
@Override
8282
public String successTypeName(UpdateControl<R> result) {
8383
String successType = "cr";
84-
if (result.isUpdateStatusSubResource()) {
84+
if (result.isUpdateStatus()) {
8585
successType = "status";
8686
}
87-
if (result.isUpdateCustomResourceAndStatusSubResource()) {
87+
if (result.isUpdateResourceAndStatus()) {
8888
successType = "both";
8989
}
9090
return successType;

Diff for: operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ReconciliationDispatcher.java

+45-26
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,6 @@ private PostExecutionControl<R> handleDispatch(ExecutionScope<R> executionScope)
8181
}
8282
}
8383

84-
private ControllerConfiguration<R> configuration() {
85-
return controller.getConfiguration();
86-
}
87-
8884
/**
8985
* Determines whether the given resource should be dispatched to the controller's
9086
* {@link Reconciler#cleanup(HasMetadata, Context)} method
@@ -100,36 +96,39 @@ private boolean shouldNotDispatchToDelete(R resource) {
10096
}
10197

10298
private PostExecutionControl<R> handleReconcile(
103-
ExecutionScope<R> executionScope, R resource, Context context) {
104-
if (configuration().useFinalizer() && !resource.hasFinalizer(configuration().getFinalizer())) {
99+
ExecutionScope<R> executionScope, R originalResource, Context context) {
100+
if (configuration().useFinalizer()
101+
&& !originalResource.hasFinalizer(configuration().getFinalizer())) {
105102
/*
106103
* We always add the finalizer if missing and the controller is configured to use a finalizer.
107104
* We execute the controller processing only for processing the event sent as a results of the
108105
* finalizer add. This will make sure that the resources are not created before there is a
109106
* finalizer.
110107
*/
111-
updateCustomResourceWithFinalizer(resource);
108+
updateCustomResourceWithFinalizer(originalResource);
112109
return PostExecutionControl.onlyFinalizerAdded();
113110
} else {
114111
try {
115112
var resourceForExecution =
116-
cloneResourceForErrorStatusHandlerIfNeeded(resource, context);
117-
return reconcileExecution(executionScope, resourceForExecution, context);
113+
cloneResourceForErrorStatusHandlerIfNeeded(originalResource, context);
114+
return reconcileExecution(executionScope, resourceForExecution, originalResource, context);
118115
} catch (RuntimeException e) {
119-
handleLastAttemptErrorStatusHandler(resource, context, e);
116+
handleLastAttemptErrorStatusHandler(originalResource, context, e);
120117
throw e;
121118
}
122119
}
123120
}
124121

125122
/**
126-
* Resource make sense only to clone for the ErrorStatusHandler. Otherwise, this operation can be
127-
* skipped since it can be memory and time-consuming. However, it needs to be cloned since it's
128-
* common that the custom resource is changed during an execution, and it's much cleaner to have
129-
* to original resource in place for status update.
123+
* Resource make sense only to clone for the ErrorStatusHandler or if the observed generation in
124+
* status is handled automatically. Otherwise, this operation can be skipped since it can be
125+
* memory and time-consuming. However, it needs to be cloned since it's common that the custom
126+
* resource is changed during an execution, and it's much cleaner to have to original resource in
127+
* place for status update.
130128
*/
131129
private R cloneResourceForErrorStatusHandlerIfNeeded(R resource, Context context) {
132-
if (isLastAttemptOfRetryAndErrorStatusHandlerPresent(context)) {
130+
if (isLastAttemptOfRetryAndErrorStatusHandlerPresent(context) ||
131+
shouldUpdateObservedGenerationAutomatically(resource)) {
133132
return controller.getConfiguration().getConfigurationService().getResourceCloner()
134133
.clone(resource);
135134
} else {
@@ -138,26 +137,29 @@ private R cloneResourceForErrorStatusHandlerIfNeeded(R resource, Context context
138137
}
139138

140139
private PostExecutionControl<R> reconcileExecution(ExecutionScope<R> executionScope,
141-
R resource, Context context) {
140+
R resourceForExecution, R originalResource, Context context) {
142141
log.debug(
143142
"Executing createOrUpdate for resource {} with version: {} with execution scope: {}",
144-
getName(resource),
145-
getVersion(resource),
143+
getName(resourceForExecution),
144+
getVersion(resourceForExecution),
146145
executionScope);
147146

148-
UpdateControl<R> updateControl = controller.reconcile(resource, context);
147+
UpdateControl<R> updateControl = controller.reconcile(resourceForExecution, context);
149148
R updatedCustomResource = null;
150-
if (updateControl.isUpdateCustomResourceAndStatusSubResource()) {
149+
if (updateControl.isUpdateResourceAndStatus()) {
151150
updatedCustomResource = updateCustomResource(updateControl.getResource());
152151
updateControl
153152
.getResource()
154153
.getMetadata()
155154
.setResourceVersion(updatedCustomResource.getMetadata().getResourceVersion());
156155
updatedCustomResource = updateStatusGenerationAware(updateControl.getResource());
157-
} else if (updateControl.isUpdateStatusSubResource()) {
156+
} else if (updateControl.isUpdateStatus()) {
158157
updatedCustomResource = updateStatusGenerationAware(updateControl.getResource());
159158
} else if (updateControl.isUpdateResource()) {
160159
updatedCustomResource = updateCustomResource(updateControl.getResource());
160+
} else if (updateControl.isNoUpdate()
161+
&& shouldUpdateObservedGenerationAutomatically(resourceForExecution)) {
162+
updatedCustomResource = updateStatusGenerationAware(originalResource);
161163
}
162164
return createPostExecutionControl(updatedCustomResource, updateControl);
163165
}
@@ -184,14 +186,27 @@ private boolean isLastAttemptOfRetryAndErrorStatusHandlerPresent(Context context
184186
}
185187
}
186188

187-
private R updateStatusGenerationAware(R customResource) {
188-
updateStatusObservedGenerationIfRequired(customResource);
189-
return customResourceFacade.updateStatus(customResource);
189+
private R updateStatusGenerationAware(R resource) {
190+
updateStatusObservedGenerationIfRequired(resource);
191+
return customResourceFacade.updateStatus(resource);
192+
}
193+
194+
private boolean shouldUpdateObservedGenerationAutomatically(R resource) {
195+
if (controller.getConfiguration().isGenerationAware()
196+
&& resource instanceof CustomResource<?, ?>) {
197+
var customResource = (CustomResource) resource;
198+
var status = customResource.getStatus();
199+
// Note that if status is null we won't update the observed generation.
200+
if (status instanceof ObservedGenerationAware) {
201+
var observedGen = ((ObservedGenerationAware) status).getObservedGeneration();
202+
var currentGen = resource.getMetadata().getGeneration();
203+
return !currentGen.equals(observedGen);
204+
}
205+
}
206+
return false;
190207
}
191208

192209
private void updateStatusObservedGenerationIfRequired(R resource) {
193-
// todo: change this to check for HasStatus (or similar) when
194-
// https://github.com/fabric8io/kubernetes-client/issues/3586 is fixed
195210
if (controller.getConfiguration().isGenerationAware()
196211
&& resource instanceof CustomResource<?, ?>) {
197212
var customResource = (CustomResource) resource;
@@ -280,6 +295,10 @@ private R replace(R resource) {
280295
return customResourceFacade.replaceWithLock(resource);
281296
}
282297

298+
private ControllerConfiguration<R> configuration() {
299+
return controller.getConfiguration();
300+
}
301+
283302
// created to support unit testing
284303
static class CustomResourceFacade<R extends HasMetadata> {
285304

Diff for: operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/ReconciliationDispatcherTest.java

+28-8
Original file line numberDiff line numberDiff line change
@@ -307,18 +307,38 @@ void reScheduleOnDeleteWithoutFinalizerRemoval() {
307307
void setObservedGenerationForStatusIfNeeded() {
308308
var observedGenResource = createObservedGenCustomResource();
309309

310-
Reconciler<ObservedGenCustomResource> lController = mock(Reconciler.class);
311-
ControllerConfiguration<ObservedGenCustomResource> lConfiguration =
310+
Reconciler<ObservedGenCustomResource> reconciler = mock(Reconciler.class);
311+
ControllerConfiguration<ObservedGenCustomResource> config =
312312
mock(ControllerConfiguration.class);
313-
CustomResourceFacade<ObservedGenCustomResource> lFacade = mock(CustomResourceFacade.class);
314-
var lDispatcher = init(observedGenResource, lController, lConfiguration, lFacade);
313+
CustomResourceFacade<ObservedGenCustomResource> facade = mock(CustomResourceFacade.class);
314+
var dispatcher = init(observedGenResource, reconciler, config, facade);
315315

316-
when(lConfiguration.isGenerationAware()).thenReturn(true);
317-
when(lController.reconcile(eq(observedGenResource), any()))
316+
when(config.isGenerationAware()).thenReturn(true);
317+
when(reconciler.reconcile(any(), any()))
318318
.thenReturn(UpdateControl.updateStatus(observedGenResource));
319-
when(lFacade.updateStatus(observedGenResource)).thenReturn(observedGenResource);
319+
when(facade.updateStatus(observedGenResource)).thenReturn(observedGenResource);
320+
321+
PostExecutionControl<ObservedGenCustomResource> control = dispatcher.handleExecution(
322+
executionScopeWithCREvent(observedGenResource));
323+
assertThat(control.getUpdatedCustomResource().get().getStatus().getObservedGeneration())
324+
.isEqualTo(1L);
325+
}
326+
327+
@Test
328+
void updatesObservedGenerationOnNoUpdateUpdateControl() {
329+
var observedGenResource = createObservedGenCustomResource();
330+
331+
Reconciler<ObservedGenCustomResource> reconciler = mock(Reconciler.class);
332+
ControllerConfiguration<ObservedGenCustomResource> config =
333+
mock(ControllerConfiguration.class);
334+
CustomResourceFacade<ObservedGenCustomResource> facade = mock(CustomResourceFacade.class);
335+
when(config.isGenerationAware()).thenReturn(true);
336+
when(reconciler.reconcile(any(), any()))
337+
.thenReturn(UpdateControl.noUpdate());
338+
when(facade.updateStatus(observedGenResource)).thenReturn(observedGenResource);
339+
var dispatcher = init(observedGenResource, reconciler, config, facade);
320340

321-
PostExecutionControl<ObservedGenCustomResource> control = lDispatcher.handleExecution(
341+
PostExecutionControl<ObservedGenCustomResource> control = dispatcher.handleExecution(
322342
executionScopeWithCREvent(observedGenResource));
323343
assertThat(control.getUpdatedCustomResource().get().getStatus().getObservedGeneration())
324344
.isEqualTo(1L);

0 commit comments

Comments
 (0)