Skip to content

Commit c058c72

Browse files
committed
refactor: compute if workflow has cleaner based on specs
1 parent 0fc616f commit c058c72

File tree

4 files changed

+74
-36
lines changed

4 files changed

+74
-36
lines changed

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.javaoperatorsdk.operator.processing.dependent;
22

3-
import java.util.*;
3+
import java.util.Optional;
44

55
import org.slf4j.Logger;
66
import org.slf4j.LoggerFactory;
@@ -9,6 +9,7 @@
99
import io.javaoperatorsdk.operator.api.reconciler.Context;
1010
import io.javaoperatorsdk.operator.api.reconciler.Ignore;
1111
import io.javaoperatorsdk.operator.api.reconciler.ResourceDiscriminator;
12+
import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter;
1213
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
1314
import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
1415
import io.javaoperatorsdk.operator.processing.dependent.Matcher.Result;
@@ -21,6 +22,7 @@ public abstract class AbstractDependentResource<R, P extends HasMetadata>
2122

2223
private final boolean creatable = this instanceof Creator;
2324
private final boolean updatable = this instanceof Updater;
25+
private final boolean deletable = this instanceof Deleter;
2426

2527
protected Creator<R, P> creator;
2628
protected Updater<R, P> updater;
@@ -172,4 +174,9 @@ protected boolean isCreatable() {
172174
protected boolean isUpdatable() {
173175
return updatable;
174176
}
177+
178+
@Override
179+
public boolean isDeletable() {
180+
return deletable;
181+
}
175182
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,18 @@ public <P extends HasMetadata> Workflow<P> createWorkflow(
5050
List<DependentResourceSpec> dependentResourceSpecs) {
5151
var orderedResourceSpecs = orderAndDetectCycles(dependentResourceSpecs);
5252
final var alreadyCreated = new ArrayList<DependentResourceNode>(orderedResourceSpecs.size());
53+
final boolean cleanerHolder[] = {false};
5354
final var nodes = orderedResourceSpecs.stream()
54-
.map(spec -> createFrom(spec, alreadyCreated))
55+
.map(spec -> createFrom(spec, alreadyCreated, cleanerHolder))
5556
.collect(Collectors.toSet());
56-
return new Workflow<>(nodes);
57+
return new Workflow<>(nodes, cleanerHolder[0]);
5758
}
5859

5960
private DependentResourceNode createFrom(DependentResourceSpec spec,
60-
List<DependentResourceNode> alreadyCreated) {
61+
List<DependentResourceNode> alreadyCreated, boolean[] cleanerHolder) {
6162
final var node = new SpecDependentResourceNode<>(spec);
6263
alreadyCreated.add(node);
64+
cleanerHolder[0] = Workflow.isDeletable(spec.getDependentResourceClass());
6365
spec.getDependsOn().forEach(depend -> {
6466
final DependentResourceNode dependsOn = alreadyCreated.stream()
6567
.filter(drn -> depend.equals(drn.getName())).findFirst()

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/Workflow.java

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -34,23 +34,40 @@ public class Workflow<P extends HasMetadata> {
3434
// it's "global" executor service shared between multiple reconciliations running parallel
3535
private final ExecutorService executorService;
3636
private boolean resolved;
37-
private boolean hasCleaner;
37+
private final boolean hasCleaner;
3838

39-
Workflow(Set<DependentResourceNode> dependentResourceNodes) {
39+
Workflow(Set<DependentResourceNode> dependentResourceNodes, boolean hasCleaner) {
4040
this(dependentResourceNodes, ExecutorServiceManager.instance().workflowExecutorService(),
41-
THROW_EXCEPTION_AUTOMATICALLY_DEFAULT, false, false);
41+
THROW_EXCEPTION_AUTOMATICALLY_DEFAULT, false, hasCleaner);
4242
}
4343

4444
Workflow(Set<DependentResourceNode> dependentResourceNodes,
4545
ExecutorService executorService, boolean throwExceptionAutomatically, boolean resolved,
4646
boolean hasCleaner) {
4747
this.executorService = executorService;
48-
this.dependentResourceNodes = dependentResourceNodes.stream()
49-
.collect(Collectors.toMap(DependentResourceNode::getName, Function.identity()));
48+
this.dependentResourceNodes = toMap(dependentResourceNodes);
5049
this.throwExceptionAutomatically = throwExceptionAutomatically;
5150
this.resolved = resolved;
5251
this.hasCleaner = hasCleaner;
53-
preprocessForReconcile();
52+
}
53+
54+
private Map<String, DependentResourceNode> toMap(
55+
Set<DependentResourceNode> dependentResourceNodes) {
56+
final var nodes = new ArrayList<>(dependentResourceNodes);
57+
bottomLevelResource.addAll(nodes);
58+
return dependentResourceNodes.stream()
59+
.peek(drn -> {
60+
// add cycle detection?
61+
if (drn.getDependsOn().isEmpty()) {
62+
topLevelResources.add(drn);
63+
} else {
64+
for (DependentResourceNode dependsOn : (List<DependentResourceNode>) drn
65+
.getDependsOn()) {
66+
bottomLevelResource.remove(dependsOn);
67+
}
68+
}
69+
})
70+
.collect(Collectors.toMap(DependentResourceNode::getName, Function.identity()));
5471
}
5572

5673
public DependentResource getDependentResourceFor(DependentResourceNode node) {
@@ -92,22 +109,6 @@ public WorkflowCleanupResult cleanup(P primary, Context<P> context) {
92109
return result;
93110
}
94111

95-
// add cycle detection?
96-
@SuppressWarnings("unchecked")
97-
private void preprocessForReconcile() {
98-
final var nodes = new ArrayList<>(dependentResourceNodes.values());
99-
bottomLevelResource.addAll(nodes);
100-
for (DependentResourceNode<?, P> node : nodes) {
101-
if (node.getDependsOn().isEmpty()) {
102-
topLevelResources.add(node);
103-
} else {
104-
for (DependentResourceNode dependsOn : node.getDependsOn()) {
105-
bottomLevelResource.remove(dependsOn);
106-
}
107-
}
108-
}
109-
}
110-
111112
Set<DependentResourceNode> getTopLevelDependentResources() {
112113
return topLevelResources;
113114
}
@@ -127,22 +128,25 @@ Map<String, DependentResourceNode> nodes() {
127128
@SuppressWarnings("unchecked")
128129
void resolve(KubernetesClient client, List<DependentResourceSpec> dependentResources) {
129130
if (!resolved) {
130-
final boolean[] cleanerHolder = {false};
131-
dependentResourceNodes.values()
132-
.forEach(drn -> {
133-
drn.resolve(client, dependentResources);
134-
final var dr = dependentResource(drn);
135-
if (dr.isDeletable()) {
136-
cleanerHolder[0] = true;
137-
}
138-
});
131+
dependentResourceNodes.values().forEach(drn -> drn.resolve(client, dependentResources));
139132
resolved = true;
140133
hasCleaner = cleanerHolder[0];
141134
}
142135
}
143136

144137
boolean hasCleaner() {
145-
throwIfUnresolved();
146138
return hasCleaner;
147139
}
140+
141+
static boolean isDeletable(Class<? extends DependentResource> drClass) {
142+
final var isDeleter = Deleter.class.isAssignableFrom(drClass);
143+
if (!isDeleter) {
144+
return false;
145+
}
146+
147+
if (KubernetesDependentResource.class.isAssignableFrom(drClass)) {
148+
return !GarbageCollected.class.isAssignableFrom(drClass);
149+
}
150+
return true;
151+
}
148152
}

operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,17 @@
55

66
import org.junit.jupiter.api.Test;
77

8+
import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter;
89
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
10+
import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected;
11+
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource;
912
import io.javaoperatorsdk.operator.sample.simple.TestCustomResource;
1013

1114
import static org.assertj.core.api.Assertions.assertThat;
15+
import static org.junit.jupiter.api.Assertions.assertFalse;
16+
import static org.junit.jupiter.api.Assertions.assertTrue;
1217
import static org.mockito.Mockito.mock;
18+
import static org.mockito.Mockito.withSettings;
1319

1420
@SuppressWarnings("rawtypes")
1521
class WorkflowTest {
@@ -54,4 +60,23 @@ void calculatesBottomLevelResources() {
5460
assertThat(bottomResources).containsExactlyInAnyOrder(dr2, independentDR);
5561
}
5662

63+
64+
@Test
65+
void isDeletableShouldWork() {
66+
var dr = mock(DependentResource.class);
67+
assertFalse(Workflow.isDeletable(dr.getClass()));
68+
69+
dr = mock(DependentResource.class, withSettings().extraInterfaces(Deleter.class));
70+
assertTrue(Workflow.isDeletable(dr.getClass()));
71+
72+
dr = mock(KubernetesDependentResource.class);
73+
assertFalse(Workflow.isDeletable(dr.getClass()));
74+
75+
dr = mock(KubernetesDependentResource.class, withSettings().extraInterfaces(Deleter.class));
76+
assertTrue(Workflow.isDeletable(dr.getClass()));
77+
78+
dr = mock(KubernetesDependentResource.class, withSettings().extraInterfaces(Deleter.class,
79+
GarbageCollected.class));
80+
assertFalse(Workflow.isDeletable(dr.getClass()));
81+
}
5782
}

0 commit comments

Comments
 (0)