Skip to content

Commit 6c119d7

Browse files
committed
test: add tests for graceful handling of null updates and finalizer removal
Signed-off-by: David Sondermann <david.sondermann@hivemq.com>
1 parent 50a5ac7 commit 6c119d7

2 files changed

Lines changed: 92 additions & 18 deletions

File tree

operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/ResourceOperationsTest.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,15 @@
2525
import io.fabric8.kubernetes.client.KubernetesClient;
2626
import io.fabric8.kubernetes.client.KubernetesClientException;
2727
import io.fabric8.kubernetes.client.dsl.MixedOperation;
28+
import io.fabric8.kubernetes.client.dsl.NamespaceableResource;
2829
import io.fabric8.kubernetes.client.dsl.Resource;
30+
import io.javaoperatorsdk.operator.MockKubernetesClient;
2931
import io.javaoperatorsdk.operator.TestUtils;
32+
import io.javaoperatorsdk.operator.api.config.BaseConfigurationService;
3033
import io.javaoperatorsdk.operator.api.config.ControllerConfiguration;
34+
import io.javaoperatorsdk.operator.api.config.ResolvedControllerConfiguration;
35+
import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration;
36+
import io.javaoperatorsdk.operator.processing.Controller;
3137
import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever;
3238
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
3339
import io.javaoperatorsdk.operator.processing.event.source.controller.ControllerEventSource;
@@ -247,6 +253,38 @@ void retriesFinalizerRemovalWithFreshResource() {
247253
verify(resourceOp, times(1)).get();
248254
}
249255

256+
@Test
257+
void removeFinalizerHandlesAlreadyDeletedTargetGracefully() {
258+
// When the underlying patch returns null, removeFinalizer must return cleanly rather
259+
// than throw
260+
var resource = TestUtils.testCustomResource1();
261+
resource.getMetadata().setResourceVersion("1");
262+
resource.addFinalizer(FINALIZER_NAME);
263+
when(context.getPrimaryResource()).thenReturn(resource);
264+
265+
// Real ControllerEventSource so eventFilteringUpdateAndCacheResource and the
266+
// TemporaryResourceCache it talks to are wired up the way they would be in production
267+
NamespaceableResource<TestCustomResource> single = mock();
268+
when(single.edit(any(UnaryOperator.class))).thenReturn(null);
269+
var client = MockKubernetesClient.client(TestCustomResource.class);
270+
when(client.resource(any(TestCustomResource.class))).thenReturn(single);
271+
272+
var eventSource = new ControllerEventSource<>(testController(client));
273+
eventSource.start();
274+
try {
275+
when(context.getClient()).thenReturn(client);
276+
var eventSourceRetriever = mock(EventSourceRetriever.class);
277+
when(eventSourceRetriever.getControllerEventSource()).thenReturn(eventSource);
278+
when(context.eventSourceRetriever()).thenReturn(eventSourceRetriever);
279+
280+
var result = resourceOperations.removeFinalizer(FINALIZER_NAME);
281+
282+
assertThat(result).isNull();
283+
} finally {
284+
eventSource.stop();
285+
}
286+
}
287+
250288
@Test
251289
void resourcePatchWithSingleEventSource() {
252290
var resource = TestUtils.testCustomResource1();
@@ -324,4 +362,28 @@ void resourcePatchThrowsWhenEventSourceIsNotManagedInformer() {
324362
assertThat(exception.getMessage()).contains("Target event source must be a subclass off");
325363
assertThat(exception.getMessage()).contains("ManagedInformerEventSource");
326364
}
365+
366+
private Controller<TestCustomResource> testController(KubernetesClient client) {
367+
var informerConfig =
368+
InformerConfiguration.builder(TestCustomResource.class)
369+
.withComparableResourceVersions(true)
370+
.withGhostResourceCacheCheckInterval(Constants.DEFAULT_GHOST_RESOURCE_CHECK_INTERVAL)
371+
.buildForController();
372+
var configuration =
373+
new ResolvedControllerConfiguration<>(
374+
"test",
375+
true,
376+
null,
377+
null,
378+
null,
379+
null,
380+
FINALIZER_NAME,
381+
null,
382+
null,
383+
new BaseConfigurationService(),
384+
informerConfig,
385+
false,
386+
null);
387+
return new Controller<>((r, ctx) -> UpdateControl.noUpdate(), configuration, client);
388+
}
327389
}

operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSourceTest.java

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,18 @@ void ghostCheckRunsConcurrentlyWithPutResource() {
412412
ghostCheckExecutor.shutdownNow();
413413
}
414414

415+
@Test
416+
void eventFilteringUpdateHandlesNullFromUpdateMethodGracefully() {
417+
// When the supplied update operation returns null, null must propagate cleanly out of
418+
// eventFilteringUpdateAndCacheResource rather than throw
419+
withRealTemporaryResourceCache();
420+
421+
var result =
422+
informerEventSource.eventFilteringUpdateAndCacheResource(testDeployment(), r -> null);
423+
424+
assertThat(result).isNull();
425+
}
426+
415427
@Test
416428
void filteringUpdateAndGhostCheckWithNamespaceChange() {
417429
var mes = mock(ManagedInformerEventSource.class);
@@ -461,26 +473,26 @@ private void assertNoEventProduced() {
461473
private void expectHandleEvent(int newResourceVersion, int oldResourceVersion) {
462474
await()
463475
.untilAsserted(
464-
() -> {
465-
verify(informerEventSource, times(1))
466-
.handleEvent(
467-
eq(ResourceAction.UPDATED),
468-
argThat(
469-
newResource -> {
470-
assertThat(newResource.getMetadata().getResourceVersion())
471-
.isEqualTo("" + newResourceVersion);
472-
return true;
473-
}),
474-
argThat(
475-
newResource -> {
476-
assertThat(newResource.getMetadata().getResourceVersion())
477-
.isEqualTo("" + oldResourceVersion);
478-
return true;
479-
}),
480-
isNull());
481-
});
476+
() ->
477+
verify(informerEventSource, times(1))
478+
.handleEvent(
479+
eq(ResourceAction.UPDATED),
480+
argThat(
481+
newResource -> {
482+
assertThat(newResource.getMetadata().getResourceVersion())
483+
.isEqualTo("" + newResourceVersion);
484+
return true;
485+
}),
486+
argThat(
487+
newResource -> {
488+
assertThat(newResource.getMetadata().getResourceVersion())
489+
.isEqualTo("" + oldResourceVersion);
490+
return true;
491+
}),
492+
isNull()));
482493
}
483494

495+
@SuppressWarnings("SameParameterValue")
484496
private CountDownLatch sendForEventFilteringUpdate(int resourceVersion) {
485497
return sendForEventFilteringUpdate(testDeployment(), resourceVersion);
486498
}

0 commit comments

Comments
 (0)