Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip: attempt to simplify workflows handling #2555

Draft
wants to merge 3 commits into
base: next
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ public Controller(Reconciler<P> reconciler,

final var managed = configurationService.getWorkflowFactory().workflowFor(configuration);
managedWorkflow = managed.resolve(kubernetesClient, configuration);
explicitWorkflowInvocation =
configuration.getWorkflowSpec().map(WorkflowSpec::isExplicitInvocation)
.orElse(false);
explicitWorkflowInvocation = configuration.getWorkflowSpec()
.map(WorkflowSpec::isExplicitInvocation)
.orElse(false);

eventSourceManager = new EventSourceManager<>(this);
eventProcessor = new EventProcessor<>(eventSourceManager, configurationService);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import io.javaoperatorsdk.operator.api.reconciler.Ignore;
import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter;
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
import io.javaoperatorsdk.operator.api.reconciler.dependent.NameSetter;
import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult;
import io.javaoperatorsdk.operator.processing.dependent.Matcher.Result;
import io.javaoperatorsdk.operator.processing.event.ResourceID;
Expand All @@ -26,16 +25,16 @@
*/
@Ignore
public abstract class AbstractDependentResource<R, P extends HasMetadata>
implements DependentResource<R, P>, NameSetter {
implements DependentResource<R, P> {
private static final Logger log = LoggerFactory.getLogger(AbstractDependentResource.class);

private final boolean creatable = this instanceof Creator;
private final boolean updatable = this instanceof Updater;
private final boolean deletable = this instanceof Deleter;
private final String name;
private final DependentResourceReconciler<R, P> dependentResourceReconciler;
protected Creator<R, P> creator;
protected Updater<R, P> updater;
protected String name;

protected AbstractDependentResource() {
this(null);
Expand Down Expand Up @@ -227,10 +226,6 @@ public String name() {
return name;
}

public void setName(String name) {
this.name = name;
}

protected boolean creatable() {
return creatable;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package io.javaoperatorsdk.operator.processing.dependent.workflow;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
Expand All @@ -10,37 +9,25 @@
import io.fabric8.kubernetes.client.KubernetesClient;
import io.javaoperatorsdk.operator.api.config.ControllerConfiguration;
import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec;
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
import io.javaoperatorsdk.operator.api.reconciler.dependent.EventSourceReferencer;
import io.javaoperatorsdk.operator.api.reconciler.dependent.NameSetter;

import static io.javaoperatorsdk.operator.api.reconciler.Constants.NO_VALUE_SET;

@SuppressWarnings("rawtypes")
public class DefaultManagedWorkflow<P extends HasMetadata> implements ManagedWorkflow<P> {
private final DefaultWorkflow<P> workflow;
private boolean resolved;
private final List<DependentResourceSpec> orderedSpecs; // todo: remove

private final Set<String> topLevelResources;
private final Set<String> bottomLevelResources;
private final List<DependentResourceSpec> orderedSpecs;
private final boolean hasCleaner;

@SuppressWarnings("unchecked")
protected DefaultManagedWorkflow(List<DependentResourceSpec> orderedSpecs, boolean hasCleaner) {
this.hasCleaner = hasCleaner;
topLevelResources = new HashSet<>(orderedSpecs.size());
bottomLevelResources = orderedSpecs.stream()
.map(DependentResourceSpec::getName)
.collect(Collectors.toSet());
this.orderedSpecs = orderedSpecs;
for (DependentResourceSpec<?, ?, ?> spec : orderedSpecs) {
// add cycle detection?
if (spec.getDependsOn().isEmpty()) {
topLevelResources.add(spec.getName());
} else {
for (String dependsOn : spec.getDependsOn()) {
bottomLevelResources.remove(dependsOn);
}
}
final var alreadyResolved = new HashMap<String, DependentResourceNode>(orderedSpecs.size());
for (DependentResourceSpec spec : orderedSpecs) {
final var node = new UnresolvedDependentResourceNode(spec);
alreadyResolved.put(spec.getName(), node);
spec.getDependsOn()
.forEach(depend -> node.addDependsOnRelation(alreadyResolved.get(depend)));
}

this.workflow = new DefaultWorkflow<>(alreadyResolved.values(), false, hasCleaner);
this.orderedSpecs = orderedSpecs; // todo: remove
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand these todos in this class. Do you still plan to work on them in this PR?

}

@Override
Expand All @@ -50,79 +37,41 @@ public List<DependentResourceSpec> getOrderedSpecs() {
}

protected Set<String> getTopLevelResources() {
return topLevelResources;
return workflow.getTopLevelDependentResources().stream().map(DependentResourceNode::name)
.collect(Collectors.toSet());
}

protected Set<String> getBottomLevelResources() {
return bottomLevelResources;
return workflow.getBottomLevelDependentResources().stream().map(DependentResourceNode::name)
.collect(Collectors.toSet());
}

List<String> nodeNames() {
return orderedSpecs.stream().map(DependentResourceSpec::getName).collect(Collectors.toList());
return workflow.getDependentResourceNodes().keySet().stream().toList();
}

@Override
public boolean hasCleaner() {
return hasCleaner;
return workflow.hasCleaner();
}

@Override
public boolean isEmpty() {
return orderedSpecs.isEmpty();
return workflow.isEmpty();
}

@Override
@SuppressWarnings("unchecked")
public Workflow<P> resolve(KubernetesClient client,
ControllerConfiguration<P> configuration) {
final var alreadyResolved = new HashMap<String, DependentResourceNode>(orderedSpecs.size());
for (DependentResourceSpec spec : orderedSpecs) {
final var dependentResource = resolve(spec, client, configuration);
final var node = new DependentResourceNode(
spec.getReconcileCondition(),
spec.getDeletePostCondition(),
spec.getReadyCondition(),
spec.getActivationCondition(),
dependentResource);
alreadyResolved.put(dependentResource.name(), node);
spec.getDependsOn()
.forEach(depend -> node.addDependsOnRelation(alreadyResolved.get(depend)));
if (!resolved) {
workflow.getDependentResourceNodes().values()
.parallelStream()
.filter(UnresolvedDependentResourceNode.class::isInstance)
.map(UnresolvedDependentResourceNode.class::cast)
.forEach(node -> node.resolve(configuration));
resolved = true;
}

final var bottom =
bottomLevelResources.stream().map(alreadyResolved::get).collect(Collectors.toSet());
final var top =
topLevelResources.stream().map(alreadyResolved::get).collect(Collectors.toSet());
return new DefaultWorkflow<>(alreadyResolved, bottom, top,
configuration.getWorkflowSpec().map(w -> !w.handleExceptionsInReconciler()).orElseThrow(),
hasCleaner);
}

@SuppressWarnings({"rawtypes", "unchecked"})
private <R> DependentResource<R, P> resolve(DependentResourceSpec<R, P, ?> spec,
KubernetesClient client,
ControllerConfiguration<P> configuration) {
final DependentResource<R, P> dependentResource =
configuration.getConfigurationService().dependentResourceFactory()
.createFrom(spec, configuration);

final var name = spec.getName();
if (name != null && !NO_VALUE_SET.equals(name) && dependentResource instanceof NameSetter) {
((NameSetter) dependentResource).setName(name);
}

spec.getUseEventSourceWithName()
.ifPresent(esName -> {
if (dependentResource instanceof EventSourceReferencer) {
((EventSourceReferencer) dependentResource).useEventSourceWithName(esName);
} else {
throw new IllegalStateException(
"DependentResource " + spec + " wants to use EventSource named " + esName
+ " but doesn't implement support for this feature by implementing "
+ EventSourceReferencer.class.getSimpleName());
}
});

return dependentResource;
return workflow;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.javaoperatorsdk.operator.processing.dependent.workflow;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
Expand Down Expand Up @@ -29,11 +30,11 @@ class DefaultWorkflow<P extends HasMetadata> implements Workflow<P> {
private final boolean throwExceptionAutomatically;
private final boolean hasCleaner;

DefaultWorkflow(Set<DependentResourceNode> dependentResourceNodes) {
DefaultWorkflow(Collection<DependentResourceNode> dependentResourceNodes) {
this(dependentResourceNodes, THROW_EXCEPTION_AUTOMATICALLY_DEFAULT, false);
}

DefaultWorkflow(Set<DependentResourceNode> dependentResourceNodes,
DefaultWorkflow(Collection<? extends DependentResourceNode> dependentResourceNodes,
boolean throwExceptionAutomatically,
boolean hasCleaner) {
this.throwExceptionAutomatically = throwExceptionAutomatically;
Expand Down Expand Up @@ -62,7 +63,8 @@ protected DefaultWorkflow(Map<String, DependentResourceNode> dependentResourceNo
}

@SuppressWarnings("unchecked")
private Map<String, DependentResourceNode> toMap(Set<DependentResourceNode> nodes) {
private Map<String, DependentResourceNode> toMap(
Collection<? extends DependentResourceNode> nodes) {
if (nodes == null || nodes.isEmpty()) {
return Collections.emptyMap();
}
Expand All @@ -77,7 +79,7 @@ private Map<String, DependentResourceNode> toMap(Set<DependentResourceNode> node
bottomLevelResource.remove(dependsOn);
}
}
map.put(node.getDependentResource().name(), node);
map.put(node.name(), node);
}
if (topLevelResources.isEmpty()) {
throw new IllegalStateException(
Expand Down Expand Up @@ -157,4 +159,8 @@ public List<DependentResource> getDependentResourcesWithoutActivationCondition()
.map(DependentResourceNode::getDependentResource)
.toList();
}

Map<String, DependentResourceNode> getDependentResourceNodes() {
return dependentResourceNodes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,51 @@
@SuppressWarnings("rawtypes")
class DependentResourceNode<R, P extends HasMetadata> {

private final List<DependentResourceNode> dependsOn = new LinkedList<>();
private final List<DependentResourceNode> parents = new LinkedList<>();
private final List<DependentResourceNode> dependsOn;
private final List<DependentResourceNode> parents;

private ConditionWithType<R, P, ?> reconcilePrecondition;
private ConditionWithType<R, P, ?> deletePostcondition;
private ConditionWithType<R, P, ?> readyPostcondition;
private ConditionWithType<R, P, ?> activationCondition;
private final DependentResource<R, P> dependentResource;
protected DependentResource<R, P> dependentResource;

protected DependentResourceNode(Condition<R, P> reconcilePrecondition,
Condition<R, P> deletePostcondition, Condition<R, P> readyPostcondition,
Condition<R, P> activationCondition) {
this(null, reconcilePrecondition, deletePostcondition, readyPostcondition, activationCondition);
}

DependentResourceNode(DependentResource<R, P> dependentResource) {
this(null, null, null, null, dependentResource);
this(dependentResource, null, null, null, null);
}

public DependentResourceNode(Condition<R, P> reconcilePrecondition,
private DependentResourceNode(DependentResource<R, P> dependentResource,
Condition<R, P> reconcilePrecondition,
Condition<R, P> deletePostcondition, Condition<R, P> readyPostcondition,
Condition<R, P> activationCondition, DependentResource<R, P> dependentResource) {
Condition<R, P> activationCondition) {
setReconcilePrecondition(reconcilePrecondition);
setDeletePostcondition(deletePostcondition);
setReadyPostcondition(readyPostcondition);
setActivationCondition(activationCondition);
this.dependentResource = dependentResource;
dependsOn = new LinkedList<>();
parents = new LinkedList<>();
}

protected DependentResourceNode(DependentResourceNode<R, P> other) {
this.dependentResource = other.dependentResource;
this.parents = other.parents;
this.dependsOn = other.dependsOn;
this.reconcilePrecondition = other.reconcilePrecondition;
this.deletePostcondition = other.deletePostcondition;
this.readyPostcondition = other.readyPostcondition;
this.activationCondition = other.activationCondition;
}

@SuppressWarnings("unchecked")
Class<? extends DependentResource<R, P>> getDependentResourceClass() {
return (Class<? extends DependentResource<R, P>>) dependentResource.getClass();
}

public List<DependentResourceNode> getDependsOn() {
Expand All @@ -54,33 +78,33 @@ public List<DependentResourceNode> getParents() {
return Optional.ofNullable(reconcilePrecondition);
}

public Optional<ConditionWithType<R, P, ?>> getDeletePostcondition() {
return Optional.ofNullable(deletePostcondition);
}

public Optional<ConditionWithType<R, P, ?>> getActivationCondition() {
return Optional.ofNullable(activationCondition);
}

public Optional<ConditionWithType<R, P, ?>> getReadyPostcondition() {
return Optional.ofNullable(readyPostcondition);
}

void setReconcilePrecondition(Condition<R, P> reconcilePrecondition) {
this.reconcilePrecondition = reconcilePrecondition == null ? null
: new ConditionWithType<>(reconcilePrecondition, Condition.Type.RECONCILE);
}

public Optional<ConditionWithType<R, P, ?>> getDeletePostcondition() {
return Optional.ofNullable(deletePostcondition);
}

void setDeletePostcondition(Condition<R, P> deletePostcondition) {
this.deletePostcondition = deletePostcondition == null ? null
: new ConditionWithType<>(deletePostcondition, Condition.Type.DELETE);
}

public Optional<ConditionWithType<R, P, ?>> getActivationCondition() {
return Optional.ofNullable(activationCondition);
}

void setActivationCondition(Condition<R, P> activationCondition) {
this.activationCondition = activationCondition == null ? null
: new ConditionWithType<>(activationCondition, Condition.Type.ACTIVATION);
}

public Optional<ConditionWithType<R, P, ?>> getReadyPostcondition() {
return Optional.ofNullable(readyPostcondition);
}

void setReadyPostcondition(Condition<R, P> readyPostcondition) {
this.readyPostcondition = readyPostcondition == null ? null
: new ConditionWithType<>(readyPostcondition, Condition.Type.READY);
Expand All @@ -90,6 +114,10 @@ public DependentResource<R, P> getDependentResource() {
return dependentResource;
}

public String name() {
return getDependentResource().name();
}

@Override
public boolean equals(Object o) {
if (this == o) {
Expand All @@ -99,12 +127,12 @@ public boolean equals(Object o) {
return false;
}
DependentResourceNode<?, ?> that = (DependentResourceNode<?, ?>) o;
return this.getDependentResource().name().equals(that.getDependentResource().name());
return name().equals(that.name());
}

@Override
public int hashCode() {
return this.getDependentResource().name().hashCode();
return name().hashCode();
}

@Override
Expand Down
Loading
Loading