Skip to content

Commit ab0c462

Browse files
metacosmcsviri
authored andcommitted
wip: record results for all conditions
Signed-off-by: Chris Laprun <claprun@redhat.com>
1 parent e372139 commit ab0c462

File tree

15 files changed

+282
-207
lines changed

15 files changed

+282
-207
lines changed

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

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package io.javaoperatorsdk.operator.processing.dependent.workflow;
22

33
import java.util.Map;
4-
import java.util.Map.Entry;
54
import java.util.Optional;
6-
import java.util.Set;
75
import java.util.concurrent.ConcurrentHashMap;
86
import java.util.concurrent.ExecutorService;
97
import java.util.concurrent.Future;
@@ -20,25 +18,24 @@
2018
@SuppressWarnings("rawtypes")
2119
abstract class AbstractWorkflowExecutor<P extends HasMetadata> {
2220

23-
protected final Workflow<P> workflow;
21+
protected final DefaultWorkflow<P> workflow;
2422
protected final P primary;
2523
protected final ResourceID primaryID;
2624
protected final Context<P> context;
25+
protected final Map<DependentResourceNode<?, P>, WorkflowResult.DetailBuilder<?>> results;
2726
/**
2827
* Covers both deleted and reconciled
2928
*/
30-
private final Set<DependentResourceNode> alreadyVisited = ConcurrentHashMap.newKeySet();
3129
private final Map<DependentResourceNode, Future<?>> actualExecutions = new ConcurrentHashMap<>();
32-
private final Map<DependentResourceNode, Exception> exceptionsDuringExecution =
33-
new ConcurrentHashMap<>();
3430
private final ExecutorService executorService;
3531

36-
public AbstractWorkflowExecutor(Workflow<P> workflow, P primary, Context<P> context) {
32+
protected AbstractWorkflowExecutor(DefaultWorkflow<P> workflow, P primary, Context<P> context) {
3733
this.workflow = workflow;
3834
this.primary = primary;
3935
this.context = context;
4036
this.primaryID = ResourceID.fromResource(primary);
4137
executorService = context.getWorkflowExecutorService();
38+
results = new ConcurrentHashMap<>(workflow.getDependentResourcesByName().size());
4239
}
4340

4441
protected abstract Logger logger();
@@ -75,11 +72,22 @@ protected boolean noMoreExecutionsScheduled() {
7572
}
7673

7774
protected boolean alreadyVisited(DependentResourceNode<?, P> dependentResourceNode) {
78-
return alreadyVisited.contains(dependentResourceNode);
75+
return results.containsKey(dependentResourceNode);
7976
}
8077

8178
protected void markAsVisited(DependentResourceNode<?, P> dependentResourceNode) {
82-
alreadyVisited.add(dependentResourceNode);
79+
createOrGetResultFor(dependentResourceNode);
80+
}
81+
82+
protected boolean postDeleteConditionNotMet(DependentResourceNode<?, P> dependentResourceNode) {
83+
final var builder = results.get(dependentResourceNode);
84+
return builder != null && builder.hasPostDeleteConditionNotMet();
85+
}
86+
87+
protected WorkflowResult.DetailBuilder createOrGetResultFor(
88+
DependentResourceNode<?, P> dependentResourceNode) {
89+
return results.computeIfAbsent(dependentResourceNode,
90+
unused -> new WorkflowResult.DetailBuilder());
8391
}
8492

8593
protected boolean isExecutingNow(DependentResourceNode<?, P> dependentResourceNode) {
@@ -94,17 +102,17 @@ protected void markAsExecuting(DependentResourceNode<?, P> dependentResourceNode
94102
protected synchronized void handleExceptionInExecutor(
95103
DependentResourceNode<?, P> dependentResourceNode,
96104
RuntimeException e) {
97-
exceptionsDuringExecution.put(dependentResourceNode, e);
105+
createOrGetResultFor(dependentResourceNode).withError(e);
98106
}
99107

100-
protected boolean isInError(DependentResourceNode<?, P> dependentResourceNode) {
101-
return exceptionsDuringExecution.containsKey(dependentResourceNode);
108+
protected boolean isNotReady(DependentResourceNode<?, P> dependentResourceNode) {
109+
final var builder = results.get(dependentResourceNode);
110+
return builder != null && builder.isNotReady();
102111
}
103112

104-
protected Map<DependentResource, Exception> getErroredDependents() {
105-
return exceptionsDuringExecution.entrySet().stream()
106-
.collect(
107-
Collectors.toMap(e -> e.getKey().getDependentResource(), Entry::getValue));
113+
protected boolean isInError(DependentResourceNode<?, P> dependentResourceNode) {
114+
final var builder = results.get(dependentResourceNode);
115+
return builder != null && builder.hasError();
108116
}
109117

110118
protected synchronized void handleNodeExecutionFinish(
@@ -116,9 +124,17 @@ protected synchronized void handleNodeExecutionFinish(
116124
}
117125
}
118126

119-
protected <R> boolean isConditionMet(Optional<Condition<R, P>> condition,
120-
DependentResource<R, P> dependentResource) {
121-
return condition.map(c -> c.isMet(dependentResource, primary, context)).orElse(true);
127+
@SuppressWarnings("unchecked")
128+
protected <R> boolean isConditionMet(
129+
Optional<DependentResourceNode.ConditionWithType<R, P, ?>> condition,
130+
DependentResourceNode<R, P> dependentResource) {
131+
final var dr = dependentResource.getDependentResource();
132+
return condition.map(c -> {
133+
final ResultCondition.Result<?> r = c.detailedIsMet(dr, primary, context);
134+
results.computeIfAbsent(dependentResource, unused -> new WorkflowResult.DetailBuilder())
135+
.withResultForCondition(c, r);
136+
return r;
137+
}).orElse(ResultCondition.Result.metWithoutResult).isSuccess();
122138
}
123139

124140
protected <R> void submit(DependentResourceNode<R, P> dependentResourceNode,
@@ -145,4 +161,10 @@ protected <R> void registerOrDeregisterEventSourceBasedOnActivation(
145161
}
146162
}
147163
}
164+
165+
protected Map<DependentResource, WorkflowResult.Detail> asDetails() {
166+
return results.entrySet().stream()
167+
.collect(
168+
Collectors.toMap(e -> e.getKey().getDependentResource(), e -> e.getValue().build()));
169+
}
148170
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
public interface Condition<R, P extends HasMetadata> {
88

9+
enum Type {
10+
ACTIVATION, DELETE, READY, RECONCILE
11+
}
12+
913
/**
1014
* Checks whether a condition holds true for a given
1115
* {@link io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource} based on the

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,10 @@ public WorkflowCleanupResult cleanup(P primary, Context<P> context) {
108108
return result;
109109
}
110110

111-
@Override
112111
public Set<DependentResourceNode> getTopLevelDependentResources() {
113112
return topLevelResources;
114113
}
115114

116-
@Override
117115
public Set<DependentResourceNode> getBottomLevelResource() {
118116
return bottomLevelResource;
119117
}

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

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.Optional;
66

77
import io.fabric8.kubernetes.api.model.HasMetadata;
8+
import io.javaoperatorsdk.operator.api.reconciler.Context;
89
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
910

1011
@SuppressWarnings("rawtypes")
@@ -13,23 +14,49 @@ class DependentResourceNode<R, P extends HasMetadata> {
1314
private final List<DependentResourceNode> dependsOn = new LinkedList<>();
1415
private final List<DependentResourceNode> parents = new LinkedList<>();
1516

16-
private Condition<R, P> reconcilePrecondition;
17-
private Condition<R, P> deletePostcondition;
18-
private Condition<R, P> readyPostcondition;
19-
private Condition<R, P> activationCondition;
17+
private ConditionWithType<R, P, ?> reconcilePrecondition;
18+
private ConditionWithType<R, P, ?> deletePostcondition;
19+
private ConditionWithType<R, P, ?> readyPostcondition;
20+
private ConditionWithType<R, P, ?> activationCondition;
2021
private final DependentResource<R, P> dependentResource;
2122

23+
static class ConditionWithType<R, P extends HasMetadata, T> implements ResultCondition<R, P, T> {
24+
private final Condition<R, P> condition;
25+
private final Type type;
26+
27+
ConditionWithType(Condition<R, P> condition, Type type) {
28+
this.condition = condition;
29+
this.type = type;
30+
}
31+
32+
public Type type() {
33+
return type;
34+
}
35+
36+
@SuppressWarnings("unchecked")
37+
@Override
38+
public Result<T> detailedIsMet(DependentResource<R, P> dependentResource, P primary,
39+
Context<P> context) {
40+
if (condition instanceof ResultCondition resultCondition) {
41+
return resultCondition.detailedIsMet(dependentResource, primary, context);
42+
} else {
43+
return ResultCondition.Result
44+
.withoutResult(condition.isMet(dependentResource, primary, context));
45+
}
46+
}
47+
}
48+
2249
DependentResourceNode(DependentResource<R, P> dependentResource) {
2350
this(null, null, null, null, dependentResource);
2451
}
2552

2653
public DependentResourceNode(Condition<R, P> reconcilePrecondition,
2754
Condition<R, P> deletePostcondition, Condition<R, P> readyPostcondition,
2855
Condition<R, P> activationCondition, DependentResource<R, P> dependentResource) {
29-
this.reconcilePrecondition = reconcilePrecondition;
30-
this.deletePostcondition = deletePostcondition;
31-
this.readyPostcondition = readyPostcondition;
32-
this.activationCondition = activationCondition;
56+
setReconcilePrecondition(reconcilePrecondition);
57+
setDeletePostcondition(deletePostcondition);
58+
setReadyPostcondition(readyPostcondition);
59+
setActivationCondition(activationCondition);
3360
this.dependentResource = dependentResource;
3461
}
3562

@@ -50,36 +77,40 @@ public List<DependentResourceNode> getParents() {
5077
return parents;
5178
}
5279

53-
public Optional<Condition<R, P>> getReconcilePrecondition() {
80+
public Optional<ConditionWithType<R, P, ?>> getReconcilePrecondition() {
5481
return Optional.ofNullable(reconcilePrecondition);
5582
}
5683

57-
public Optional<Condition<R, P>> getDeletePostcondition() {
84+
public Optional<ConditionWithType<R, P, ?>> getDeletePostcondition() {
5885
return Optional.ofNullable(deletePostcondition);
5986
}
6087

61-
public Optional<Condition<R, P>> getActivationCondition() {
88+
public Optional<ConditionWithType<R, P, ?>> getActivationCondition() {
6289
return Optional.ofNullable(activationCondition);
6390
}
6491

92+
public Optional<ConditionWithType<R, P, ?>> getReadyPostcondition() {
93+
return Optional.ofNullable(readyPostcondition);
94+
}
95+
6596
void setReconcilePrecondition(Condition<R, P> reconcilePrecondition) {
66-
this.reconcilePrecondition = reconcilePrecondition;
97+
this.reconcilePrecondition = reconcilePrecondition == null ? null
98+
: new ConditionWithType<>(reconcilePrecondition, Condition.Type.RECONCILE);
6799
}
68100

69101
void setDeletePostcondition(Condition<R, P> cleanupCondition) {
70-
this.deletePostcondition = cleanupCondition;
102+
this.deletePostcondition = deletePostcondition == null ? null
103+
: new ConditionWithType<>(deletePostcondition, Condition.Type.DELETE);
71104
}
72105

73106
void setActivationCondition(Condition<R, P> activationCondition) {
74-
this.activationCondition = activationCondition;
75-
}
76-
77-
public Optional<Condition<R, P>> getReadyPostcondition() {
78-
return Optional.ofNullable(readyPostcondition);
107+
this.activationCondition = activationCondition == null ? null
108+
: new ConditionWithType<>(activationCondition, Condition.Type.ACTIVATION);
79109
}
80110

81111
void setReadyPostcondition(Condition<R, P> readyPostcondition) {
82-
this.readyPostcondition = readyPostcondition;
112+
this.readyPostcondition = readyPostcondition == null ? null
113+
: new ConditionWithType<>(readyPostcondition, Condition.Type.READY);
83114
}
84115

85116
public DependentResource<R, P> getDependentResource() {

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

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

33
import io.fabric8.kubernetes.api.model.HasMetadata;
4-
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
54

65
abstract class NodeExecutor<R, P extends HasMetadata> implements Runnable {
76

@@ -17,9 +16,7 @@ protected NodeExecutor(DependentResourceNode<R, P> dependentResourceNode,
1716
@Override
1817
public void run() {
1918
try {
20-
var dependentResource = dependentResourceNode.getDependentResource();
21-
22-
doRun(dependentResourceNode, dependentResource);
19+
doRun(dependentResourceNode);
2320

2421
} catch (RuntimeException e) {
2522
workflowExecutor.handleExceptionInExecutor(dependentResourceNode, e);
@@ -28,6 +25,5 @@ public void run() {
2825
}
2926
}
3027

31-
protected abstract void doRun(DependentResourceNode<R, P> dependentResourceNode,
32-
DependentResource<R, P> dependentResource);
28+
protected abstract void doRun(DependentResourceNode<R, P> dependentResourceNode);
3329
}

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,34 @@ default boolean isMet(DependentResource<R, P> dependentResource, P primary, Cont
1515
}
1616

1717
interface Result<T> {
18+
Result metWithoutResult = new Result() {
19+
@Override
20+
public Object getResult() {
21+
return NULL;
22+
}
23+
24+
@Override
25+
public boolean isSuccess() {
26+
return true;
27+
}
28+
};
29+
30+
Result unmetWithoutResult = new Result() {
31+
@Override
32+
public Object getResult() {
33+
return NULL;
34+
}
35+
36+
@Override
37+
public boolean isSuccess() {
38+
return false;
39+
}
40+
};
41+
42+
static Result withoutResult(boolean success) {
43+
return success ? metWithoutResult : unmetWithoutResult;
44+
}
45+
1846
T getResult();
1947

2048
boolean isSuccess();

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

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import java.util.Collections;
44
import java.util.List;
55
import java.util.Map;
6-
import java.util.Set;
76

87
import io.fabric8.kubernetes.api.model.HasMetadata;
98
import io.javaoperatorsdk.operator.api.reconciler.Context;
@@ -21,16 +20,6 @@ default WorkflowCleanupResult cleanup(P primary, Context<P> context) {
2120
throw new UnsupportedOperationException("Implement this");
2221
}
2322

24-
@SuppressWarnings("rawtypes")
25-
default Set<DependentResourceNode> getTopLevelDependentResources() {
26-
return Collections.emptySet();
27-
}
28-
29-
@SuppressWarnings("rawtypes")
30-
default Set<DependentResourceNode> getBottomLevelResource() {
31-
return Collections.emptySet();
32-
}
33-
3423
default boolean hasCleaner() {
3524
return false;
3625
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ public WorkflowBuilder<P> withThrowExceptionFurther(boolean throwExceptionFurthe
8282
}
8383

8484
public Workflow<P> build() {
85+
return buildAsDefaultWorkflow();
86+
}
87+
88+
DefaultWorkflow<P> buildAsDefaultWorkflow() {
8589
return new DefaultWorkflow(new HashSet<>(dependentResourceNodes.values()),
8690
throwExceptionAutomatically, isCleaner);
8791
}

0 commit comments

Comments
 (0)