-
Notifications
You must be signed in to change notification settings - Fork 218
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: return result when calling workflow explicitly (#2601)
Signed-off-by: Chris Laprun <claprun@redhat.com>
- Loading branch information
Showing
13 changed files
with
315 additions
and
230 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
193 changes: 193 additions & 0 deletions
193
...in/java/io/javaoperatorsdk/operator/processing/dependent/workflow/BaseWorkflowResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
package io.javaoperatorsdk.operator.processing.dependent.workflow; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Map.Entry; | ||
import java.util.Optional; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
import io.javaoperatorsdk.operator.AggregatedOperatorException; | ||
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; | ||
import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; | ||
|
||
@SuppressWarnings("rawtypes") | ||
class BaseWorkflowResult implements WorkflowResult { | ||
private final Map<DependentResource, Detail<?>> results; | ||
private Boolean hasErroredDependents; | ||
|
||
BaseWorkflowResult(Map<DependentResource, Detail<?>> results) { | ||
this.results = results; | ||
} | ||
|
||
@Override | ||
public Map<DependentResource, Exception> getErroredDependents() { | ||
return getErroredDependentsStream() | ||
.collect(Collectors.toMap(Entry::getKey, entry -> entry.getValue().error)); | ||
} | ||
|
||
private Stream<Entry<DependentResource, Detail<?>>> getErroredDependentsStream() { | ||
return results.entrySet().stream().filter(entry -> entry.getValue().error != null); | ||
} | ||
|
||
protected Map<DependentResource, Detail<?>> results() { | ||
return results; | ||
} | ||
|
||
@Override | ||
public Optional<DependentResource> getDependentResourceByName(String name) { | ||
if (name == null || name.isEmpty()) { | ||
return Optional.empty(); | ||
} | ||
return results.keySet().stream().filter(dr -> dr.name().equals(name)).findFirst(); | ||
} | ||
|
||
@Override | ||
public <T> Optional<T> getDependentConditionResult(DependentResource dependentResource, | ||
Condition.Type conditionType, Class<T> expectedResultType) { | ||
if (dependentResource == null) { | ||
return Optional.empty(); | ||
} | ||
|
||
final var result = new Object[1]; | ||
try { | ||
return Optional.ofNullable(results().get(dependentResource)) | ||
.flatMap(detail -> detail.getResultForConditionWithType(conditionType)) | ||
.map(r -> result[0] = r.getDetail()) | ||
.map(expectedResultType::cast); | ||
} catch (Exception e) { | ||
throw new IllegalArgumentException("Condition " + | ||
"result " + result[0] + | ||
" for Dependent " + dependentResource.name() + " doesn't match expected type " | ||
+ expectedResultType.getSimpleName(), e); | ||
} | ||
} | ||
|
||
protected List<DependentResource> listFilteredBy( | ||
Function<Detail, Boolean> filter) { | ||
return results.entrySet().stream() | ||
.filter(e -> filter.apply(e.getValue())) | ||
.map(Map.Entry::getKey) | ||
.toList(); | ||
} | ||
|
||
@Override | ||
public boolean erroredDependentsExist() { | ||
if (hasErroredDependents == null) { | ||
hasErroredDependents = !getErroredDependents().isEmpty(); | ||
} | ||
return hasErroredDependents; | ||
} | ||
|
||
@Override | ||
public void throwAggregateExceptionIfErrorsPresent() { | ||
if (erroredDependentsExist()) { | ||
throw new AggregatedOperatorException("Exception(s) during workflow execution.", | ||
getErroredDependentsStream() | ||
.collect(Collectors.toMap(e -> e.getKey().name(), e -> e.getValue().error))); | ||
} | ||
} | ||
|
||
@SuppressWarnings("UnusedReturnValue") | ||
static class DetailBuilder<R> { | ||
private Exception error; | ||
private ReconcileResult<R> reconcileResult; | ||
private DetailedCondition.Result activationConditionResult; | ||
private DetailedCondition.Result deletePostconditionResult; | ||
private DetailedCondition.Result readyPostconditionResult; | ||
private DetailedCondition.Result reconcilePostconditionResult; | ||
private boolean deleted; | ||
private boolean visited; | ||
private boolean markedForDelete; | ||
|
||
Detail<R> build() { | ||
return new Detail<>(error, reconcileResult, activationConditionResult, | ||
deletePostconditionResult, readyPostconditionResult, reconcilePostconditionResult, | ||
deleted, visited, markedForDelete); | ||
} | ||
|
||
DetailBuilder<R> withResultForCondition( | ||
ConditionWithType conditionWithType, | ||
DetailedCondition.Result conditionResult) { | ||
switch (conditionWithType.type()) { | ||
case ACTIVATION -> activationConditionResult = conditionResult; | ||
case DELETE -> deletePostconditionResult = conditionResult; | ||
case READY -> readyPostconditionResult = conditionResult; | ||
case RECONCILE -> reconcilePostconditionResult = conditionResult; | ||
default -> | ||
throw new IllegalStateException("Unexpected condition type: " + conditionWithType); | ||
} | ||
return this; | ||
} | ||
|
||
DetailBuilder<R> withError(Exception error) { | ||
this.error = error; | ||
return this; | ||
} | ||
|
||
DetailBuilder<R> withReconcileResult(ReconcileResult<R> reconcileResult) { | ||
this.reconcileResult = reconcileResult; | ||
return this; | ||
} | ||
|
||
DetailBuilder<R> markAsDeleted() { | ||
this.deleted = true; | ||
return this; | ||
} | ||
|
||
public boolean hasError() { | ||
return error != null; | ||
} | ||
|
||
public boolean hasPostDeleteConditionNotMet() { | ||
return deletePostconditionResult != null && !deletePostconditionResult.isSuccess(); | ||
} | ||
|
||
public boolean isReady() { | ||
return readyPostconditionResult == null || readyPostconditionResult.isSuccess(); | ||
} | ||
|
||
DetailBuilder<R> markAsVisited() { | ||
visited = true; | ||
return this; | ||
} | ||
|
||
public boolean isVisited() { | ||
return visited; | ||
} | ||
|
||
public boolean isMarkedForDelete() { | ||
return markedForDelete; | ||
} | ||
|
||
DetailBuilder<R> markForDelete() { | ||
markedForDelete = true; | ||
return this; | ||
} | ||
} | ||
|
||
|
||
record Detail<R>(Exception error, ReconcileResult<R> reconcileResult, | ||
DetailedCondition.Result activationConditionResult, | ||
DetailedCondition.Result deletePostconditionResult, | ||
DetailedCondition.Result readyPostconditionResult, | ||
DetailedCondition.Result reconcilePostconditionResult, | ||
boolean deleted, boolean visited, boolean markedForDelete) { | ||
|
||
boolean isConditionWithTypeMet(Condition.Type conditionType) { | ||
return getResultForConditionWithType(conditionType).map(DetailedCondition.Result::isSuccess) | ||
.orElse(true); | ||
} | ||
|
||
Optional<DetailedCondition.Result<?>> getResultForConditionWithType( | ||
Condition.Type conditionType) { | ||
return switch (conditionType) { | ||
case ACTIVATION -> Optional.ofNullable(activationConditionResult); | ||
case DELETE -> Optional.ofNullable(deletePostconditionResult); | ||
case READY -> Optional.ofNullable(readyPostconditionResult); | ||
case RECONCILE -> Optional.ofNullable(reconcilePostconditionResult); | ||
}; | ||
} | ||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
.../javaoperatorsdk/operator/processing/dependent/workflow/DefaultWorkflowCleanupResult.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package io.javaoperatorsdk.operator.processing.dependent.workflow; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
|
||
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; | ||
|
||
@SuppressWarnings("rawtypes") | ||
class DefaultWorkflowCleanupResult extends BaseWorkflowResult implements WorkflowCleanupResult { | ||
private Boolean allPostConditionsMet; | ||
|
||
DefaultWorkflowCleanupResult(Map<DependentResource, BaseWorkflowResult.Detail<?>> results) { | ||
super(results); | ||
} | ||
|
||
public List<DependentResource> getDeleteCalledOnDependents() { | ||
return listFilteredBy(BaseWorkflowResult.Detail::deleted); | ||
} | ||
|
||
public List<DependentResource> getPostConditionNotMetDependents() { | ||
return listFilteredBy(detail -> !detail.isConditionWithTypeMet(Condition.Type.DELETE)); | ||
} | ||
|
||
public boolean allPostConditionsMet() { | ||
if (allPostConditionsMet == null) { | ||
allPostConditionsMet = getPostConditionNotMetDependents().isEmpty(); | ||
} | ||
return allPostConditionsMet; | ||
} | ||
} |
Oops, something went wrong.