Skip to content

Commit bb3a8c3

Browse files
authored
fix: isLastAttempt on error handler correct if max attempt 0 (#1595)
1 parent da20264 commit bb3a8c3

File tree

2 files changed

+42
-2
lines changed

2 files changed

+42
-2
lines changed

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,17 @@ class ReconciliationDispatcher<P extends HasMetadata> {
3838

3939
private final Controller<P> controller;
4040
private final CustomResourceFacade<P> customResourceFacade;
41+
// this is to handle corner case, when there is a retry, but it is actually limited to 0.
42+
// Usually for testing purposes.
43+
private final boolean retryConfigurationHasZeroAttempts;
4144

4245
ReconciliationDispatcher(Controller<P> controller,
4346
CustomResourceFacade<P> customResourceFacade) {
4447
this.controller = controller;
4548
this.customResourceFacade = customResourceFacade;
49+
50+
var retry = controller.getConfiguration().getRetry();
51+
retryConfigurationHasZeroAttempts = retry == null || retry.initExecution().isLastAttempt();
4652
}
4753

4854
public ReconciliationDispatcher(Controller<P> controller) {
@@ -173,7 +179,9 @@ public int getAttemptCount() {
173179

174180
@Override
175181
public boolean isLastAttempt() {
176-
return controller.getConfiguration().getRetry() == null;
182+
// check also if the retry is limited to 0
183+
return retryConfigurationHasZeroAttempts ||
184+
controller.getConfiguration().getRetry() == null;
177185
}
178186
});
179187
((DefaultContext<P>) context).setRetryInfo(retryInfo);

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.junit.jupiter.api.BeforeEach;
1212
import org.junit.jupiter.api.Test;
1313
import org.mockito.ArgumentCaptor;
14+
import org.mockito.ArgumentMatcher;
1415
import org.mockito.ArgumentMatchers;
1516
import org.mockito.stubbing.Answer;
1617

@@ -110,7 +111,10 @@ private <R extends HasMetadata> ReconciliationDispatcher<R> init(R customResourc
110111
when(configuration.getFinalizerName()).thenReturn(DEFAULT_FINALIZER);
111112
when(configuration.getName()).thenReturn("EventDispatcherTestController");
112113
when(configuration.getResourceClass()).thenReturn(resourceClass);
113-
when(configuration.getRetry()).thenReturn(new GenericRetry());
114+
// needed so the retry can be predefined
115+
if (configuration.getRetry() == null) {
116+
when(configuration.getRetry()).thenReturn(new GenericRetry());
117+
}
114118
when(configuration.maxReconciliationInterval())
115119
.thenReturn(Optional.of(Duration.ofHours(RECONCILIATION_MAX_INTERVAL)));
116120

@@ -600,6 +604,34 @@ void errorStatusHandlerCanPatchResource() {
600604
any(), any());
601605
}
602606

607+
@Test
608+
void ifRetryLimitedToZeroMaxAttemptsErrorHandlerGetsCorrectLastAttempt() {
609+
var configuration =
610+
MockControllerConfiguration
611+
.forResource((Class<TestCustomResource>) testCustomResource.getClass());
612+
when(configuration.getRetry()).thenReturn(new GenericRetry().setMaxAttempts(0));
613+
reconciliationDispatcher =
614+
init(testCustomResource, reconciler, configuration, customResourceFacade, false);
615+
616+
reconciler.reconcile = (r, c) -> {
617+
throw new IllegalStateException("Error Status Test");
618+
};
619+
var mockErrorHandler = mock(ErrorStatusHandler.class);
620+
when(mockErrorHandler.updateErrorStatus(any(), any(), any()))
621+
.thenReturn(ErrorStatusUpdateControl.noStatusUpdate());
622+
reconciler.errorHandler = mockErrorHandler;
623+
624+
reconciliationDispatcher.handleExecution(
625+
new ExecutionScope(
626+
testCustomResource, null));
627+
628+
verify(mockErrorHandler, times(1)).updateErrorStatus(any(),
629+
ArgumentMatchers.argThat((ArgumentMatcher<Context<TestCustomResource>>) context -> {
630+
var retryInfo = context.getRetryInfo().orElseThrow();
631+
return retryInfo.isLastAttempt();
632+
}), any());
633+
}
634+
603635
@Test
604636
void canSkipSchedulingMaxDelayIf() {
605637
testCustomResource.addFinalizer(DEFAULT_FINALIZER);

0 commit comments

Comments
 (0)