Skip to content

Commit d4201fe

Browse files
authored
Improvement/handle namespace deletion on finalizer gracefully (#1988)
1 parent 03e06ce commit d4201fe

File tree

2 files changed

+33
-10
lines changed

2 files changed

+33
-10
lines changed

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

+15-6
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,6 @@ private void updatePostExecutionControlWithReschedule(
280280
baseControl.getScheduleDelay().ifPresent(postExecutionControl::withReSchedule);
281281
}
282282

283-
284283
private PostExecutionControl<P> handleCleanup(P resource,
285284
Context<P> context) {
286285
log.debug(
@@ -295,12 +294,23 @@ private PostExecutionControl<P> handleCleanup(P resource,
295294
// cleanup is finished, nothing left to done
296295
final var finalizerName = configuration().getFinalizerName();
297296
if (deleteControl.isRemoveFinalizer() && resource.hasFinalizer(finalizerName)) {
298-
P customResource = conflictRetryingUpdate(resource, r -> r.removeFinalizer(finalizerName));
297+
P customResource = conflictRetryingUpdate(resource, r -> {
298+
// the operator might not be allowed to retrieve the resource on a retry, e.g. when its
299+
// permissions are removed by deleting the namespace concurrently
300+
if (r == null) {
301+
log.warn(
302+
"Could not remove finalizer on null resource: {} with version: {}",
303+
getUID(resource),
304+
getVersion(resource));
305+
return false;
306+
}
307+
return r.removeFinalizer(finalizerName);
308+
});
299309
return PostExecutionControl.customResourceFinalizerRemoved(customResource);
300310
}
301311
}
302312
log.debug(
303-
"Skipping finalizer remove for resource: {} with version: {}. delete control: {}, uses finalizer: {} ",
313+
"Skipping finalizer remove for resource: {} with version: {}. delete control: {}, uses finalizer: {}",
304314
getUID(resource),
305315
getVersion(resource),
306316
deleteControl,
@@ -385,15 +395,14 @@ public R updateResource(R resource) {
385395
getName(resource),
386396
resource.getMetadata().getResourceVersion());
387397
return resource(resource).lockResourceVersion(resource.getMetadata().getResourceVersion())
388-
.replace();
398+
.update();
389399
}
390400

391-
@SuppressWarnings({"rawtypes", "unchecked"})
392401
public R updateStatus(R resource) {
393402
log.trace("Updating status for resource: {}", resource);
394403
return resource(resource)
395404
.lockResourceVersion()
396-
.replaceStatus();
405+
.updateStatus();
397406
}
398407

399408
public R patchStatus(R resource, R originalResource) {

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

+18-4
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,24 @@ void retriesFinalizerRemovalWithFreshResource() {
240240
verify(customResourceFacade, times(1)).getResource(any(), any());
241241
}
242242

243+
@Test
244+
void nullResourceIsGracefullyHandledOnFinalizerRemovalRetry() {
245+
// simulate the operator not able or not be allowed to get the custom resource during the retry
246+
// of the finalizer removal
247+
testCustomResource.addFinalizer(DEFAULT_FINALIZER);
248+
markForDeletion(testCustomResource);
249+
when(customResourceFacade.updateResource(any()))
250+
.thenThrow(new KubernetesClientException(null, 409, null));
251+
when(customResourceFacade.getResource(any(), any())).thenReturn(null);
252+
253+
var postExecControl =
254+
reconciliationDispatcher.handleExecution(executionScopeWithCREvent(testCustomResource));
255+
256+
assertThat(postExecControl.isFinalizerRemoved()).isTrue();
257+
verify(customResourceFacade, times(1)).updateResource(testCustomResource);
258+
verify(customResourceFacade, times(1)).getResource(any(), any());
259+
}
260+
243261
@Test
244262
void throwsExceptionIfFinalizerRemovalRetryExceeded() {
245263
testCustomResource.addFinalizer(DEFAULT_FINALIZER);
@@ -307,8 +325,6 @@ void doesNotAddFinalizerIfConfiguredNotTo() {
307325
assertEquals(0, testCustomResource.getMetadata().getFinalizers().size());
308326
}
309327

310-
311-
312328
@Test
313329
void doesNotRemovesTheSetFinalizerIfTheDeleteNotMethodInstructsIt() {
314330
testCustomResource.addFinalizer(DEFAULT_FINALIZER);
@@ -590,7 +606,6 @@ void errorStatusHandlerCanPatchResource() {
590606
reconciler.errorHandler =
591607
(r, ri, e) -> ErrorStatusUpdateControl.patchStatus(testCustomResource);
592608

593-
594609
reconciliationDispatcher.handleExecution(
595610
new ExecutionScope(null).setResource(testCustomResource));
596611

@@ -673,7 +688,6 @@ TestCustomResource createResourceWithFinalizer() {
673688
return resourceWithFinalizer;
674689
}
675690

676-
677691
private void removeFinalizers(CustomResource customResource) {
678692
customResource.getMetadata().getFinalizers().clear();
679693
}

0 commit comments

Comments
 (0)