Skip to content

Commit 9d36fb3

Browse files
authored
fix: retrieve DependentResource based on name instead of class (#1058)
1 parent 69f7a0b commit 9d36fb3

File tree

39 files changed

+723
-347
lines changed

39 files changed

+723
-347
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package io.javaoperatorsdk.operator;
2+
3+
import java.util.Arrays;
4+
import java.util.Collections;
5+
import java.util.List;
6+
7+
public class AggregatedOperatorException extends OperatorException {
8+
private final List<Exception> causes;
9+
10+
public AggregatedOperatorException(String message, Exception... exceptions) {
11+
super(message);
12+
this.causes = exceptions != null ? Arrays.asList(exceptions) : Collections.emptyList();
13+
}
14+
15+
public AggregatedOperatorException(String message, List<Exception> exceptions) {
16+
super(message);
17+
this.causes = exceptions != null ? exceptions : Collections.emptyList();
18+
}
19+
20+
public List<Exception> getAggregatedExceptions() {
21+
return causes;
22+
}
23+
}

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

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package io.javaoperatorsdk.operator.api.config;
22

33
import java.time.Duration;
4-
import java.util.ArrayList;
54
import java.util.Collections;
6-
import java.util.List;
5+
import java.util.HashMap;
6+
import java.util.Map;
77
import java.util.Optional;
88
import java.util.Set;
99
import java.util.function.Function;
@@ -26,7 +26,7 @@ public class AnnotationControllerConfiguration<R extends HasMetadata>
2626

2727
protected final Reconciler<R> reconciler;
2828
private final ControllerConfiguration annotation;
29-
private List<DependentResourceSpec<?, ?>> specs;
29+
private Map<String, DependentResourceSpec<?, ?>> specs;
3030

3131
public AnnotationControllerConfiguration(Reconciler<R> reconciler) {
3232
this.reconciler = reconciler;
@@ -135,15 +135,15 @@ public static <T> T valueOrDefault(
135135

136136
@SuppressWarnings({"rawtypes", "unchecked"})
137137
@Override
138-
public List<DependentResourceSpec<?, ?>> getDependentResources() {
138+
public Map<String, DependentResourceSpec<?, ?>> getDependentResources() {
139139
if (specs == null) {
140140
final var dependents =
141141
valueOrDefault(annotation, ControllerConfiguration::dependents, new Dependent[] {});
142142
if (dependents.length == 0) {
143-
return Collections.emptyList();
143+
return Collections.emptyMap();
144144
}
145145

146-
specs = new ArrayList<>(dependents.length);
146+
specs = new HashMap<>(dependents.length);
147147
for (Dependent dependent : dependents) {
148148
Object config = null;
149149
final Class<? extends DependentResource> dependentType = dependent.type();
@@ -159,9 +159,18 @@ public static <T> T valueOrDefault(
159159
config =
160160
new KubernetesDependentResourceConfig(namespaces, labelSelector);
161161
}
162-
specs.add(new DependentResourceSpec(dependentType, config));
162+
var name = dependent.name();
163+
if (name.isBlank()) {
164+
name = DependentResource.defaultNameFor(dependentType);
165+
}
166+
final DependentResourceSpec<?, ?> spec = specs.get(name);
167+
if (spec != null) {
168+
throw new IllegalArgumentException(
169+
"A DependentResource named: " + name + " already exists: " + spec);
170+
}
171+
specs.put(name, new DependentResourceSpec(dependentType, config, name));
163172
}
164173
}
165-
return specs;
174+
return Collections.unmodifiableMap(specs);
166175
}
167176
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import java.time.Duration;
44
import java.util.Collections;
5-
import java.util.List;
5+
import java.util.Map;
66
import java.util.Optional;
77

88
import io.fabric8.kubernetes.api.model.HasMetadata;
@@ -46,8 +46,8 @@ default ResourceEventFilter<R> getEventFilter() {
4646
return ResourceEventFilters.passthrough();
4747
}
4848

49-
default List<DependentResourceSpec<?, ?>> getDependentResources() {
50-
return Collections.emptyList();
49+
default Map<String, DependentResourceSpec<?, ?>> getDependentResources() {
50+
return Collections.emptyMap();
5151
}
5252

5353
default Optional<Duration> reconciliationMaxInterval() {

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

Lines changed: 14 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package io.javaoperatorsdk.operator.api.config;
22

33
import java.time.Duration;
4+
import java.util.HashMap;
45
import java.util.HashSet;
56
import java.util.List;
6-
import java.util.Optional;
7+
import java.util.Map;
78
import java.util.Set;
89

910
import io.fabric8.kubernetes.api.model.HasMetadata;
1011
import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec;
11-
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
1212
import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter;
1313

1414
@SuppressWarnings({"rawtypes", "unchecked", "unused"})
@@ -22,7 +22,7 @@ public class ControllerConfigurationOverrider<R extends HasMetadata> {
2222
private ResourceEventFilter<R> customResourcePredicate;
2323
private final ControllerConfiguration<R> original;
2424
private Duration reconciliationMaxInterval;
25-
private final List<DependentResourceSpec<?, ?>> dependentResourceSpecs;
25+
private final Map<String, DependentResourceSpec<?, ?>> dependentResourceSpecs;
2626

2727
private ControllerConfigurationOverrider(ControllerConfiguration<R> original) {
2828
finalizer = original.getFinalizerName();
@@ -32,7 +32,8 @@ private ControllerConfigurationOverrider(ControllerConfiguration<R> original) {
3232
labelSelector = original.getLabelSelector();
3333
customResourcePredicate = original.getEventFilter();
3434
reconciliationMaxInterval = original.reconciliationMaxInterval().orElse(null);
35-
dependentResourceSpecs = original.getDependentResources();
35+
// make the original specs modifiable
36+
dependentResourceSpecs = new HashMap<>(original.getDependentResources());
3637
this.original = original;
3738
}
3839

@@ -89,37 +90,17 @@ public ControllerConfigurationOverrider<R> withReconciliationMaxInterval(
8990
return this;
9091
}
9192

92-
public void replaceDependentResourceConfig(
93-
Class<? extends DependentResource<?, R>> dependentResourceClass,
93+
public ControllerConfigurationOverrider<R> replacingNamedDependentResourceConfig(String name,
9494
Object dependentResourceConfig) {
95-
96-
var currentConfig =
97-
findConfigForDependentResourceClass(dependentResourceClass);
98-
if (currentConfig.isEmpty()) {
99-
throw new IllegalStateException("Cannot find DependentResource config for class: "
100-
+ dependentResourceClass);
101-
}
102-
dependentResourceSpecs.remove(currentConfig.get());
103-
dependentResourceSpecs
104-
.add(new DependentResourceSpec(dependentResourceClass, dependentResourceConfig));
105-
}
106-
107-
public void addNewDependentResourceConfig(DependentResourceSpec dependentResourceSpec) {
108-
var currentConfig =
109-
findConfigForDependentResourceClass(dependentResourceSpec.getDependentResourceClass());
110-
if (currentConfig.isPresent()) {
111-
throw new IllegalStateException(
112-
"Config already present for class: "
113-
+ dependentResourceSpec.getDependentResourceClass());
95+
final var currentConfig = dependentResourceSpecs.get(name);
96+
if (currentConfig == null) {
97+
throw new IllegalArgumentException("Cannot find a DependentResource named: " + name);
11498
}
115-
dependentResourceSpecs.add(dependentResourceSpec);
116-
}
117-
118-
private Optional<DependentResourceSpec<?, ?>> findConfigForDependentResourceClass(
119-
Class<? extends DependentResource> dependentResourceClass) {
120-
return dependentResourceSpecs.stream()
121-
.filter(dc -> dc.getDependentResourceClass().equals(dependentResourceClass))
122-
.findFirst();
99+
dependentResourceSpecs.remove(name);
100+
dependentResourceSpecs.put(name,
101+
new DependentResourceSpec(currentConfig.getDependentResourceClass(),
102+
dependentResourceConfig, name));
103+
return this;
123104
}
124105

125106
public ControllerConfiguration<R> build() {

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22

33
import java.time.Duration;
44
import java.util.Collections;
5-
import java.util.List;
5+
import java.util.Map;
66
import java.util.Optional;
77
import java.util.Set;
88

99
import io.fabric8.kubernetes.api.model.HasMetadata;
1010
import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec;
1111
import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEventFilter;
1212

13-
@SuppressWarnings("rawtypes")
1413
public class DefaultControllerConfiguration<R extends HasMetadata>
1514
extends DefaultResourceConfiguration<R>
1615
implements ControllerConfiguration<R> {
@@ -22,7 +21,7 @@ public class DefaultControllerConfiguration<R extends HasMetadata>
2221
private final boolean generationAware;
2322
private final RetryConfiguration retryConfiguration;
2423
private final ResourceEventFilter<R> resourceEventFilter;
25-
private final List<DependentResourceSpec<?, ?>> dependents;
24+
private final Map<String, DependentResourceSpec<?, ?>> dependents;
2625
private final Duration reconciliationMaxInterval;
2726

2827
// NOSONAR constructor is meant to provide all information
@@ -38,7 +37,7 @@ public DefaultControllerConfiguration(
3837
ResourceEventFilter<R> resourceEventFilter,
3938
Class<R> resourceClass,
4039
Duration reconciliationMaxInterval,
41-
List<DependentResourceSpec<?, ?>> dependents) {
40+
Map<String, DependentResourceSpec<?, ?>> dependents) {
4241
super(labelSelector, resourceClass, namespaces);
4342
this.associatedControllerClassName = associatedControllerClassName;
4443
this.name = name;
@@ -52,7 +51,7 @@ public DefaultControllerConfiguration(
5251
: retryConfiguration;
5352
this.resourceEventFilter = resourceEventFilter;
5453

55-
this.dependents = dependents != null ? dependents : Collections.emptyList();
54+
this.dependents = dependents != null ? dependents : Collections.emptyMap();
5655
}
5756

5857
@Override
@@ -91,7 +90,7 @@ public ResourceEventFilter<R> getEventFilter() {
9190
}
9291

9392
@Override
94-
public List<DependentResourceSpec<?, ?>> getDependentResources() {
93+
public Map<String, DependentResourceSpec<?, ?>> getDependentResources() {
9594
return dependents;
9695
}
9796

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

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

3+
import java.util.Objects;
34
import java.util.Optional;
45

56
import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource;
@@ -10,9 +11,13 @@ public class DependentResourceSpec<T extends DependentResource<?, ?>, C> {
1011

1112
private final C dependentResourceConfig;
1213

13-
public DependentResourceSpec(Class<T> dependentResourceClass, C dependentResourceConfig) {
14+
private final String name;
15+
16+
public DependentResourceSpec(Class<T> dependentResourceClass, C dependentResourceConfig,
17+
String name) {
1418
this.dependentResourceClass = dependentResourceClass;
1519
this.dependentResourceConfig = dependentResourceConfig;
20+
this.name = name;
1621
}
1722

1823
public Class<T> getDependentResourceClass() {
@@ -22,4 +27,32 @@ public Class<T> getDependentResourceClass() {
2227
public Optional<C> getDependentResourceConfiguration() {
2328
return Optional.ofNullable(dependentResourceConfig);
2429
}
30+
31+
public String getName() {
32+
return name;
33+
}
34+
35+
@Override
36+
public String toString() {
37+
return "DependentResourceSpec{ name='" + name +
38+
"', type=" + dependentResourceClass.getCanonicalName() +
39+
", config=" + dependentResourceConfig + '}';
40+
}
41+
42+
@Override
43+
public boolean equals(Object o) {
44+
if (this == o) {
45+
return true;
46+
}
47+
if (o == null || getClass() != o.getClass()) {
48+
return false;
49+
}
50+
DependentResourceSpec<?, ?> that = (DependentResourceSpec<?, ?>) o;
51+
return name.equals(that.name);
52+
}
53+
54+
@Override
55+
public int hashCode() {
56+
return Objects.hash(name);
57+
}
2558
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ public DefaultContext(RetryInfo retryInfo, Controller<P> controller, P primaryRe
2020
this.controller = controller;
2121
this.primaryResource = primaryResource;
2222
this.controllerConfiguration = controller.getConfiguration();
23-
this.managedDependentResourceContext = new ManagedDependentResourceContext(
24-
controller.getDependents());
23+
this.managedDependentResourceContext = new ManagedDependentResourceContext();
2524
}
2625

2726
@Override

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

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

3-
import java.util.List;
3+
import java.util.Map;
44

55
import io.fabric8.kubernetes.api.model.HasMetadata;
66
import io.javaoperatorsdk.operator.processing.event.source.EventSource;
@@ -20,6 +20,6 @@ public interface EventSourceInitializer<P extends HasMetadata> {
2020
* sources
2121
* @return list of event sources to register
2222
*/
23-
List<EventSource> prepareEventSources(EventSourceContext<P> context);
23+
Map<String, EventSource> prepareEventSources(EventSourceContext<P> context);
2424

2525
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
package io.javaoperatorsdk.operator.api.reconciler.dependent;
22

3+
import static io.javaoperatorsdk.operator.api.reconciler.Constants.EMPTY_STRING;
4+
35
public @interface Dependent {
46

7+
@SuppressWarnings("rawtypes")
58
Class<? extends DependentResource> type();
9+
10+
String name() default EMPTY_STRING;
611
}

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,49 @@
55
import io.fabric8.kubernetes.api.model.HasMetadata;
66
import io.javaoperatorsdk.operator.api.reconciler.Context;
77

8+
/**
9+
* An interface to implement and provide dependent resource support.
10+
*
11+
* @param <R> the dependent resource type
12+
* @param <P> the associated primary resource type
13+
*/
814
public interface DependentResource<R, P extends HasMetadata> {
15+
16+
/**
17+
* Reconciles the dependent resource given the desired primary state
18+
*
19+
* @param primary the primary resource for which we want to reconcile the dependent state
20+
* @param context {@link Context} providing useful contextual information
21+
* @return a {@link ReconcileResult} providing information about the reconciliation result
22+
*/
923
ReconcileResult<R> reconcile(P primary, Context<P> context);
1024

25+
/**
26+
* Retrieves the dependent resource associated with the specified primary one
27+
*
28+
* @param primaryResource the primary resource for which we want to retrieve the secondary
29+
* resource
30+
* @return an {@link Optional} containing the secondary resource or {@link Optional#empty()} if it
31+
* doesn't exist
32+
*/
1133
Optional<R> getResource(P primaryResource);
34+
35+
/**
36+
* Retrieves the resource type associated with this DependentResource
37+
*
38+
* @return the resource type associated with this DependentResource
39+
*/
40+
Class<R> resourceType();
41+
42+
/**
43+
* Computes a default name for the specified DependentResource class
44+
*
45+
* @param dependentResourceClass the DependentResource class for which we want to compute a
46+
* default name
47+
* @return the default name for the specified DependentResource class
48+
*/
49+
@SuppressWarnings("rawtypes")
50+
static String defaultNameFor(Class<? extends DependentResource> dependentResourceClass) {
51+
return dependentResourceClass.getCanonicalName();
52+
}
1253
}

0 commit comments

Comments
 (0)