Skip to content

Commit f5f0a60

Browse files
authored
fix: explicit workflow invocation uses the same resource intance that reconcile api (#2686)
Signed-off-by: Attila Mészáros <[email protected]>
1 parent 1e3ac83 commit f5f0a60

File tree

3 files changed

+45
-18
lines changed

3 files changed

+45
-18
lines changed

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public <T> Optional<T> getSecondaryResource(Class<T> expectedType, String eventS
7373
* If a workflow has an activation condition there can be event sources which are only
7474
* registered if the activation condition holds, but to provide a consistent API we return an
7575
* Optional instead of throwing an exception.
76-
*
76+
*
7777
* Note that not only the resource which has an activation condition might not be registered
7878
* but dependents which depend on it.
7979
*/
@@ -116,4 +116,8 @@ public DefaultContext<P> setRetryInfo(RetryInfo retryInfo) {
116116
this.retryInfo = retryInfo;
117117
return this;
118118
}
119+
120+
public P getPrimaryResource() {
121+
return primaryResource;
122+
}
119123
}

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

+12-12
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ private PostExecutionControl<P> handleDispatch(ExecutionScope<P> executionScope)
8686
}
8787

8888
Context<P> context =
89-
new DefaultContext<>(executionScope.getRetryInfo(), controller, originalResource);
89+
new DefaultContext<>(executionScope.getRetryInfo(), controller, resourceForExecution);
9090
if (markedForDeletion) {
9191
return handleCleanup(resourceForExecution, originalResource, context);
9292
} else {
@@ -234,29 +234,29 @@ private void updatePostExecutionControlWithReschedule(
234234
baseControl.getScheduleDelay().ifPresent(postExecutionControl::withReSchedule);
235235
}
236236

237-
private PostExecutionControl<P> handleCleanup(P resource,
237+
private PostExecutionControl<P> handleCleanup(P resourceForExecution,
238238
P originalResource, Context<P> context) {
239239
if (log.isDebugEnabled()) {
240240
log.debug(
241241
"Executing delete for resource: {} with version: {}",
242-
ResourceID.fromResource(resource),
243-
getVersion(resource));
242+
ResourceID.fromResource(resourceForExecution),
243+
getVersion(resourceForExecution));
244244
}
245-
DeleteControl deleteControl = controller.cleanup(resource, context);
245+
DeleteControl deleteControl = controller.cleanup(resourceForExecution, context);
246246
final var useFinalizer = controller.useFinalizer();
247247
if (useFinalizer) {
248248
// note that we don't reschedule here even if instructed. Removing finalizer means that
249-
// cleanup is finished, nothing left to done
249+
// cleanup is finished, nothing left to be done
250250
final var finalizerName = configuration().getFinalizerName();
251-
if (deleteControl.isRemoveFinalizer() && resource.hasFinalizer(finalizerName)) {
252-
P customResource = conflictRetryingPatch(resource, originalResource, r -> {
251+
if (deleteControl.isRemoveFinalizer() && resourceForExecution.hasFinalizer(finalizerName)) {
252+
P customResource = conflictRetryingPatch(resourceForExecution, originalResource, r -> {
253253
// the operator might not be allowed to retrieve the resource on a retry, e.g. when its
254254
// permissions are removed by deleting the namespace concurrently
255255
if (r == null) {
256256
log.warn(
257257
"Could not remove finalizer on null resource: {} with version: {}",
258-
getUID(resource),
259-
getVersion(resource));
258+
getUID(resourceForExecution),
259+
getVersion(resourceForExecution));
260260
return false;
261261
}
262262
return r.removeFinalizer(finalizerName);
@@ -266,8 +266,8 @@ private PostExecutionControl<P> handleCleanup(P resource,
266266
}
267267
log.debug(
268268
"Skipping finalizer remove for resource: {} with version: {}. delete control: {}, uses finalizer: {}",
269-
getUID(resource),
270-
getVersion(resource),
269+
getUID(resourceForExecution),
270+
getVersion(resourceForExecution),
271271
deleteControl,
272272
useFinalizer);
273273
PostExecutionControl<P> postExecutionControl = PostExecutionControl.defaultDispatch();

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

+28-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import java.util.function.BiFunction;
88
import java.util.function.Supplier;
99

10+
import io.fabric8.kubernetes.client.utils.KubernetesSerialization;
11+
import io.javaoperatorsdk.operator.api.reconciler.DefaultContext;
1012
import org.junit.jupiter.api.BeforeEach;
1113
import org.junit.jupiter.api.Test;
1214
import org.mockito.ArgumentCaptor;
@@ -68,6 +70,9 @@ void setup() {
6870
}
6971

7072
static void initConfigService(boolean useSSA) {
73+
initConfigService(useSSA,true);
74+
}
75+
static void initConfigService(boolean useSSA, boolean noCloning) {
7176
/*
7277
* We need this for mock reconcilers to properly generate the expected UpdateControl: without
7378
* this, calls such as `when(reconciler.reconcile(eq(testCustomResource),
@@ -77,14 +82,18 @@ static void initConfigService(boolean useSSA) {
7782
*/
7883
configurationService =
7984
ConfigurationService.newOverriddenConfigurationService(new BaseConfigurationService(),
80-
overrider -> overrider.checkingCRDAndValidateLocalModel(false)
85+
overrider -> overrider.checkingCRDAndValidateLocalModel(false)
86+
8187
.withResourceCloner(new Cloner() {
8288
@Override
8389
public <R extends HasMetadata> R clone(R object) {
90+
if (noCloning) {
8491
return object;
92+
}else {
93+
return new KubernetesSerialization().clone(object);
8594
}
86-
})
87-
.withUseSSAToPatchPrimaryResource(useSSA));
95+
}})
96+
.withUseSSAToPatchPrimaryResource(useSSA));
8897
}
8998

9099
private <R extends HasMetadata> ReconciliationDispatcher<R> init(R customResource,
@@ -659,10 +668,24 @@ void reSchedulesFromErrorHandler() {
659668
}
660669

661670
@Test
662-
void addsFinalizerToPatchWithSSA() {
671+
void reconcilerContextUsesTheSameInstanceOfResourceAsParam() {
672+
initConfigService(false,false);
663673

664-
}
674+
final ReconciliationDispatcher<TestCustomResource> dispatcher =
675+
init(testCustomResource, reconciler, null, customResourceFacade, true);
665676

677+
testCustomResource.addFinalizer(DEFAULT_FINALIZER);
678+
ArgumentCaptor<DefaultContext> contextArgumentCaptor = ArgumentCaptor.forClass(DefaultContext.class);
679+
ArgumentCaptor<TestCustomResource> customResourceCaptor = ArgumentCaptor.forClass(TestCustomResource.class);
680+
681+
dispatcher.handleExecution(executionScopeWithCREvent(testCustomResource));
682+
verify(reconciler, times(1))
683+
.reconcile(customResourceCaptor.capture(), contextArgumentCaptor.capture());
684+
685+
assertThat(contextArgumentCaptor.getValue().getPrimaryResource())
686+
.isSameAs(customResourceCaptor.getValue())
687+
.isNotSameAs(testCustomResource);
688+
}
666689

667690
private ObservedGenCustomResource createObservedGenCustomResource() {
668691
ObservedGenCustomResource observedGenCustomResource = new ObservedGenCustomResource();

0 commit comments

Comments
 (0)