Skip to content

Commit 03eb78d

Browse files
authored
fix: explicit workflow invovation cleanup issue (#2680)
Signed-off-by: Attila Mészáros <[email protected]>
1 parent a46c94d commit 03eb78d

File tree

2 files changed

+72
-2
lines changed

2 files changed

+72
-2
lines changed

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,9 @@ public DeleteControl execute() throws Exception {
194194
WorkflowCleanupResult workflowCleanupResult = null;
195195

196196
// The cleanup is called also when explicit invocation is true, but the cleaner is not
197-
// implemented
198-
if (managedWorkflow.hasCleaner() || !explicitWorkflowInvocation) {
197+
// implemented, also in case when explicit invocation is false, but there is cleaner
198+
// implemented.
199+
if (managedWorkflow.hasCleaner() && (!explicitWorkflowInvocation || !isCleaner)) {
199200
workflowCleanupResult = managedWorkflow.cleanup(resource, context);
200201
}
201202

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

+69
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,33 @@
11
package io.javaoperatorsdk.operator.processing;
22

3+
import java.util.Optional;
4+
35
import org.junit.jupiter.api.Test;
6+
import org.junit.jupiter.params.ParameterizedTest;
7+
import org.junit.jupiter.params.provider.CsvSource;
48

59
import io.fabric8.kubernetes.api.model.Secret;
610
import io.javaoperatorsdk.operator.MockKubernetesClient;
711
import io.javaoperatorsdk.operator.api.config.BaseConfigurationService;
812
import io.javaoperatorsdk.operator.api.config.ConfigurationService;
913
import io.javaoperatorsdk.operator.api.config.MockControllerConfiguration;
14+
import io.javaoperatorsdk.operator.api.config.workflow.WorkflowSpec;
1015
import io.javaoperatorsdk.operator.api.reconciler.Cleaner;
16+
import io.javaoperatorsdk.operator.api.reconciler.DefaultContext;
17+
import io.javaoperatorsdk.operator.api.reconciler.DeleteControl;
1118
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
19+
import io.javaoperatorsdk.operator.processing.dependent.workflow.ManagedWorkflow;
20+
import io.javaoperatorsdk.operator.processing.dependent.workflow.ManagedWorkflowFactory;
21+
import io.javaoperatorsdk.operator.processing.dependent.workflow.Workflow;
22+
import io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowCleanupResult;
1223
import io.javaoperatorsdk.operator.sample.simple.TestCustomResource;
1324

25+
import static io.javaoperatorsdk.operator.api.monitoring.Metrics.NOOP;
1426
import static org.assertj.core.api.Assertions.assertThat;
27+
import static org.mockito.ArgumentMatchers.any;
1528
import static org.mockito.Mockito.mock;
1629
import static org.mockito.Mockito.never;
30+
import static org.mockito.Mockito.times;
1731
import static org.mockito.Mockito.verify;
1832
import static org.mockito.Mockito.when;
1933
import static org.mockito.Mockito.withSettings;
@@ -61,4 +75,59 @@ void usesFinalizerIfThereIfReconcilerImplementsCleaner() {
6175

6276
assertThat(controller.useFinalizer()).isTrue();
6377
}
78+
79+
@ParameterizedTest
80+
@CsvSource({
81+
"true, true, true, false",
82+
"true, true, false, true",
83+
"false, true, true, true",
84+
"false, true, false, true",
85+
"true, false, true, false",
86+
})
87+
void callsCleanupOnWorkflowWhenHasCleanerAndReconcilerIsNotCleaner(boolean reconcilerIsCleaner,
88+
boolean workflowIsCleaner,
89+
boolean isExplicitWorkflowInvocation,
90+
boolean workflowCleanerExecuted) throws Exception {
91+
92+
Reconciler reconciler;
93+
if (reconcilerIsCleaner) {
94+
reconciler = mock(Reconciler.class, withSettings().extraInterfaces(Cleaner.class));
95+
} else {
96+
reconciler = mock(Reconciler.class);
97+
}
98+
99+
final var configuration = MockControllerConfiguration.forResource(Secret.class);
100+
101+
if (reconciler instanceof Cleaner<?> cleaner) {
102+
when(cleaner.cleanup(any(), any())).thenReturn(DeleteControl.noFinalizerRemoval());
103+
}
104+
105+
var configurationService = mock(ConfigurationService.class);
106+
var mockWorkflowFactory = mock(ManagedWorkflowFactory.class);
107+
var mockManagedWorkflow = mock(ManagedWorkflow.class);
108+
109+
when(configuration.getConfigurationService()).thenReturn(configurationService);
110+
var workflowSpec = mock(WorkflowSpec.class);
111+
when(workflowSpec.isExplicitInvocation()).thenReturn(isExplicitWorkflowInvocation);
112+
when(configuration.getWorkflowSpec()).thenReturn(Optional.of(workflowSpec));
113+
when(configurationService.getMetrics()).thenReturn(NOOP);
114+
when(configurationService.getWorkflowFactory()).thenReturn(mockWorkflowFactory);
115+
when(mockWorkflowFactory.workflowFor(any())).thenReturn(mockManagedWorkflow);
116+
var managedWorkflowMock = workflow(workflowIsCleaner);
117+
when(mockManagedWorkflow.resolve(any(), any())).thenReturn(managedWorkflowMock);
118+
119+
final var controller = new Controller<Secret>(reconciler, configuration,
120+
MockKubernetesClient.client(Secret.class));
121+
122+
controller.cleanup(new Secret(), new DefaultContext<>(null, controller, new Secret()));
123+
124+
verify(managedWorkflowMock, times(workflowCleanerExecuted ? 1 : 0)).cleanup(any(), any());
125+
}
126+
127+
private Workflow workflow(boolean hasCleaner) {
128+
var workflow = mock(Workflow.class);
129+
when(workflow.cleanup(any(), any())).thenReturn(mock(WorkflowCleanupResult.class));
130+
when(workflow.hasCleaner()).thenReturn(hasCleaner);
131+
return workflow;
132+
}
64133
}

0 commit comments

Comments
 (0)