Skip to content

Commit e34ec59

Browse files
committed
feat: workflow Integration with API (dependent annotations, context) (#1257)
1 parent 4b7e5d1 commit e34ec59

File tree

58 files changed

+1533
-399
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1533
-399
lines changed

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AnnotationControllerConfiguration.java

+32-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.javaoperatorsdk.operator.api.config;
22

3+
import java.lang.reflect.InvocationTargetException;
34
import java.time.Duration;
45
import java.util.Arrays;
56
import java.util.Collections;
@@ -19,9 +20,11 @@
1920
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
2021
import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent;
2122
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
23+
import io.javaoperatorsdk.operator.api.reconciler.dependent.VoidCondition;
2224
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent;
2325
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource;
2426
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfig;
27+
import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
2528
import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter;
2629
import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilters;
2730

@@ -172,19 +175,46 @@ public List<DependentResourceSpec> getDependentResources() {
172175
}
173176

174177
final var name = getName(dependent, dependentType);
175-
final var spec = specsMap.get(name);
178+
var spec = specsMap.get(name);
176179
if (spec != null) {
177180
throw new IllegalArgumentException(
178181
"A DependentResource named: " + name + " already exists: " + spec);
179182
}
180-
specsMap.put(name, new DependentResourceSpec(dependentType, config, name));
183+
spec = new DependentResourceSpec(dependentType, config, name);
184+
spec.setDependsOn(Set.of(dependent.dependsOn()));
185+
addConditions(spec, dependent);
186+
specsMap.put(name, spec);
181187
}
182188

183189
specs = specsMap.values().stream().collect(Collectors.toUnmodifiableList());
184190
}
185191
return specs;
186192
}
187193

194+
@SuppressWarnings("unchecked")
195+
private void addConditions(DependentResourceSpec spec, Dependent dependent) {
196+
if (dependent.deletePostcondition() != VoidCondition.class) {
197+
spec.setDeletePostCondition(instantiateCondition(dependent.deletePostcondition()));
198+
}
199+
if (dependent.readyPostcondition() != VoidCondition.class) {
200+
spec.setReadyPostcondition(instantiateCondition(dependent.readyPostcondition()));
201+
}
202+
if (dependent.reconcilePrecondition() != VoidCondition.class) {
203+
spec.setReconcilePrecondition(instantiateCondition(dependent.reconcilePrecondition()));
204+
}
205+
}
206+
207+
private Condition<?, ?> instantiateCondition(Class<? extends Condition> condition) {
208+
try {
209+
return condition.getDeclaredConstructor().newInstance();
210+
} catch (InstantiationException
211+
| IllegalAccessException
212+
| InvocationTargetException
213+
| NoSuchMethodException e) {
214+
throw new OperatorException(e);
215+
}
216+
}
217+
188218
private String getName(Dependent dependent, Class<? extends DependentResource> dependentType) {
189219
var name = dependent.name();
190220
if (name.isBlank()) {

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceProvider.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@
77
* to the ConfigurationService is via the reconciliation context.
88
*/
99
public class ConfigurationServiceProvider {
10-
static final ConfigurationService DEFAULT =
11-
new BaseConfigurationService(Utils.loadFromProperties());
1210
private static ConfigurationService instance;
13-
private static ConfigurationService defaultConfigurationService = DEFAULT;
11+
private static ConfigurationService defaultConfigurationService = createDefault();
1412
private static boolean alreadyConfigured = false;
1513

1614
private ConfigurationServiceProvider() {}
@@ -64,8 +62,12 @@ synchronized static ConfigurationService getDefault() {
6462
}
6563

6664
public synchronized static void reset() {
67-
defaultConfigurationService = DEFAULT;
65+
defaultConfigurationService = createDefault();
6866
instance = null;
6967
alreadyConfigured = false;
7068
}
69+
70+
static ConfigurationService createDefault() {
71+
return new BaseConfigurationService(Utils.loadFromProperties());
72+
}
7173
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java

+9-4
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,10 @@ public ControllerConfiguration<R> build() {
140140
// if the spec has a config and it's a KubernetesDependentResourceConfig, update the
141141
// namespaces if needed, otherwise, just return the existing spec
142142
final Optional<?> maybeConfig = spec.getDependentResourceConfiguration();
143-
final Class<?> drClass = drsEntry.getValue().getDependentResourceClass();
144143
return maybeConfig.filter(KubernetesDependentResourceConfig.class::isInstance)
145144
.map(KubernetesDependentResourceConfig.class::cast)
146145
.filter(Predicate.not(KubernetesDependentResourceConfig::wereNamespacesConfigured))
147-
.map(c -> updateSpec(drsEntry.getKey(), drClass, c))
146+
.map(c -> updateSpec(drsEntry.getKey(), spec, c))
148147
.orElse(drsEntry.getValue());
149148
}).collect(Collectors.toUnmodifiableList());
150149

@@ -164,9 +163,15 @@ public ControllerConfiguration<R> build() {
164163
}
165164

166165
@SuppressWarnings({"rawtypes", "unchecked"})
167-
private DependentResourceSpec<?, ?> updateSpec(String name, Class<?> drClass,
166+
private DependentResourceSpec<?, ?> updateSpec(String name, DependentResourceSpec spec,
168167
KubernetesDependentResourceConfig c) {
169-
return new DependentResourceSpec(drClass, c.setNamespaces(namespaces), name);
168+
var res = new DependentResourceSpec(spec.getDependentResourceClass(),
169+
c.setNamespaces(namespaces), name);
170+
res.setReadyPostcondition(spec.getReadyCondition());
171+
res.setReconcilePrecondition(spec.getReconcileCondition());
172+
res.setDeletePostCondition(spec.getDeletePostCondition());
173+
res.setDependsOn(spec.getDependsOn());
174+
return res;
170175
}
171176

172177
public static <R extends HasMetadata> ControllerConfigurationOverrider<R> override(

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java

+53
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package io.javaoperatorsdk.operator.api.config.dependent;
22

3+
import java.util.HashSet;
34
import java.util.Objects;
45
import java.util.Optional;
6+
import java.util.Set;
57

68
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
9+
import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
710

811
public class DependentResourceSpec<T extends DependentResource<?, ?>, C> {
912

@@ -13,6 +16,14 @@ public class DependentResourceSpec<T extends DependentResource<?, ?>, C> {
1316

1417
private final String name;
1518

19+
private Set<String> dependsOn;
20+
21+
private Condition<?, ?> readyCondition;
22+
23+
private Condition<?, ?> reconcileCondition;
24+
25+
private Condition<?, ?> deletePostCondition;
26+
1627
public DependentResourceSpec(Class<T> dependentResourceClass, C dependentResourceConfig,
1728
String name) {
1829
this.dependentResourceClass = dependentResourceClass;
@@ -55,4 +66,46 @@ public boolean equals(Object o) {
5566
public int hashCode() {
5667
return Objects.hash(name);
5768
}
69+
70+
public Set<String> getDependsOn() {
71+
if (dependsOn == null) {
72+
dependsOn = new HashSet<>(0);
73+
}
74+
return dependsOn;
75+
}
76+
77+
public DependentResourceSpec<T, C> setDependsOn(Set<String> dependsOn) {
78+
this.dependsOn = dependsOn;
79+
return this;
80+
}
81+
82+
@SuppressWarnings("rawtypes")
83+
public Condition getReadyCondition() {
84+
return readyCondition;
85+
}
86+
87+
public DependentResourceSpec<T, C> setReadyPostcondition(Condition<?, ?> readyCondition) {
88+
this.readyCondition = readyCondition;
89+
return this;
90+
}
91+
92+
@SuppressWarnings("rawtypes")
93+
public Condition getReconcileCondition() {
94+
return reconcileCondition;
95+
}
96+
97+
public DependentResourceSpec<T, C> setReconcilePrecondition(Condition<?, ?> reconcileCondition) {
98+
this.reconcileCondition = reconcileCondition;
99+
return this;
100+
}
101+
102+
@SuppressWarnings("rawtypes")
103+
public Condition getDeletePostCondition() {
104+
return deletePostCondition;
105+
}
106+
107+
public DependentResourceSpec<T, C> setDeletePostCondition(Condition<?, ?> deletePostCondition) {
108+
this.deletePostCondition = deletePostCondition;
109+
return this;
110+
}
58111
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
public interface Cleaner<P extends HasMetadata> {
66

77
/**
8-
* Note that this method turns on automatic finalizer usage.
8+
* This method turns on automatic finalizer usage.
99
*
1010
* The implementation should delete the associated component(s). This method is called when an
1111
* object is marked for deletion. After it's executed the custom resource finalizer is

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

+5-3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import io.fabric8.kubernetes.api.model.HasMetadata;
88
import io.javaoperatorsdk.operator.api.config.ControllerConfiguration;
9+
import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.DefaultManagedDependentResourceContext;
910
import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ManagedDependentResourceContext;
1011
import io.javaoperatorsdk.operator.processing.Controller;
1112

@@ -15,14 +16,14 @@ public class DefaultContext<P extends HasMetadata> implements Context<P> {
1516
private final Controller<P> controller;
1617
private final P primaryResource;
1718
private final ControllerConfiguration<P> controllerConfiguration;
18-
private final ManagedDependentResourceContext managedDependentResourceContext;
19+
private final DefaultManagedDependentResourceContext defaultManagedDependentResourceContext;
1920

2021
public DefaultContext(RetryInfo retryInfo, Controller<P> controller, P primaryResource) {
2122
this.retryInfo = retryInfo;
2223
this.controller = controller;
2324
this.primaryResource = primaryResource;
2425
this.controllerConfiguration = controller.getConfiguration();
25-
this.managedDependentResourceContext = new ManagedDependentResourceContext();
26+
this.defaultManagedDependentResourceContext = new DefaultManagedDependentResourceContext();
2627
}
2728

2829
@Override
@@ -52,8 +53,9 @@ public ControllerConfiguration<P> getControllerConfiguration() {
5253
return controllerConfiguration;
5354
}
5455

56+
@Override
5557
public ManagedDependentResourceContext managedDependentResourceContext() {
56-
return managedDependentResourceContext;
58+
return defaultManagedDependentResourceContext;
5759
}
5860

5961
public DefaultContext<P> setRetryInfo(RetryInfo retryInfo) {

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

+13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package io.javaoperatorsdk.operator.api.reconciler.dependent;
22

3+
import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
4+
35
import static io.javaoperatorsdk.operator.api.reconciler.Constants.NO_VALUE_SET;
46

57
public @interface Dependent {
@@ -8,4 +10,15 @@
810
Class<? extends DependentResource> type();
911

1012
String name() default NO_VALUE_SET;
13+
14+
@SuppressWarnings("rawtypes")
15+
Class<? extends Condition> readyPostcondition() default VoidCondition.class;
16+
17+
@SuppressWarnings("rawtypes")
18+
Class<? extends Condition> reconcilePrecondition() default VoidCondition.class;
19+
20+
@SuppressWarnings("rawtypes")
21+
Class<? extends Condition> deletePostcondition() default VoidCondition.class;
22+
23+
String[] dependsOn() default {};
1124
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package io.javaoperatorsdk.operator.api.reconciler.dependent;
2+
3+
import io.fabric8.kubernetes.api.model.HasMetadata;
4+
import io.javaoperatorsdk.operator.api.reconciler.Context;
5+
import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition;
6+
7+
/** Used as default value for Condition in annotations */
8+
@SuppressWarnings("rawtypes")
9+
public class VoidCondition implements Condition {
10+
@Override
11+
public boolean isMet(DependentResource dependentResource, HasMetadata primary, Context context) {
12+
throw new IllegalStateException("This is a placeholder class, should not be called");
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package io.javaoperatorsdk.operator.api.reconciler.dependent.managed;
2+
3+
import java.util.Optional;
4+
import java.util.concurrent.ConcurrentHashMap;
5+
6+
import io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowCleanupResult;
7+
import io.javaoperatorsdk.operator.processing.dependent.workflow.WorkflowReconcileResult;
8+
9+
@SuppressWarnings("rawtypes")
10+
public class DefaultManagedDependentResourceContext implements ManagedDependentResourceContext {
11+
12+
private WorkflowReconcileResult workflowReconcileResult;
13+
private WorkflowCleanupResult workflowCleanupResult;
14+
private final ConcurrentHashMap attributes = new ConcurrentHashMap();
15+
16+
@Override
17+
public <T> Optional<T> get(Object key, Class<T> expectedType) {
18+
return Optional.ofNullable(attributes.get(key))
19+
.filter(expectedType::isInstance)
20+
.map(expectedType::cast);
21+
}
22+
23+
@Override
24+
@SuppressWarnings("unchecked")
25+
public <T> T put(Object key, T value) {
26+
if (value == null) {
27+
return (T) Optional.ofNullable(attributes.remove(key));
28+
}
29+
return (T) Optional.ofNullable(attributes.put(key, value));
30+
}
31+
32+
@Override
33+
@SuppressWarnings("unused")
34+
public <T> T getMandatory(Object key, Class<T> expectedType) {
35+
return get(key, expectedType).orElseThrow(() -> new IllegalStateException(
36+
"Mandatory attribute (key: " + key + ", type: " + expectedType.getName()
37+
+ ") is missing or not of the expected type"));
38+
}
39+
40+
public DefaultManagedDependentResourceContext setWorkflowExecutionResult(
41+
WorkflowReconcileResult workflowReconcileResult) {
42+
this.workflowReconcileResult = workflowReconcileResult;
43+
return this;
44+
}
45+
46+
public DefaultManagedDependentResourceContext setWorkflowCleanupResult(
47+
WorkflowCleanupResult workflowCleanupResult) {
48+
this.workflowCleanupResult = workflowCleanupResult;
49+
return this;
50+
}
51+
52+
@Override
53+
public Optional<WorkflowReconcileResult> getWorkflowReconcileResult() {
54+
return Optional.ofNullable(workflowReconcileResult);
55+
}
56+
57+
@Override
58+
public Optional<WorkflowCleanupResult> getWorkflowCleanupResult() {
59+
return Optional.ofNullable(workflowCleanupResult);
60+
}
61+
}

0 commit comments

Comments
 (0)