Skip to content

Commit 6b071e0

Browse files
committed
feat: @ControllerConfiguration annotation is optional
Signed-off-by: Attila Mészáros <csviri@gmail.com>
1 parent 5d833f1 commit 6b071e0

File tree

2 files changed

+94
-67
lines changed

2 files changed

+94
-67
lines changed

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

Lines changed: 90 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import io.fabric8.kubernetes.api.model.HasMetadata;
1515
import io.fabric8.kubernetes.client.KubernetesClient;
1616
import io.fabric8.kubernetes.client.informers.cache.ItemStore;
17-
import io.javaoperatorsdk.operator.OperatorException;
1817
import io.javaoperatorsdk.operator.ReconcilerUtils;
1918
import io.javaoperatorsdk.operator.api.config.Utils.Configurator;
2019
import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec;
@@ -32,7 +31,6 @@
3231
import io.javaoperatorsdk.operator.processing.retry.Retry;
3332

3433
import static io.javaoperatorsdk.operator.api.config.ControllerConfiguration.CONTROLLER_NAME_AS_FIELD_MANAGER;
35-
import static io.javaoperatorsdk.operator.api.reconciler.Constants.DEFAULT_NAMESPACES_SET;
3634

3735
public class BaseConfigurationService extends AbstractConfigurationService {
3836

@@ -94,101 +92,124 @@ public <R extends HasMetadata> ControllerConfiguration<R> getConfigurationFor(
9492
return config;
9593
}
9694

97-
@SuppressWarnings({"unchecked", "rawtypes"})
95+
@SuppressWarnings({"rawtypes"})
9896
protected <P extends HasMetadata> ControllerConfiguration<P> configFor(Reconciler<P> reconciler) {
9997
final var annotation = reconciler.getClass().getAnnotation(
10098
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration.class);
10199

102-
if (annotation == null) {
103-
throw new OperatorException(
104-
"Missing mandatory @"
105-
+ io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration.class
106-
.getSimpleName()
107-
+
108-
" annotation for reconciler: " + reconciler);
100+
ResolvedControllerConfiguration<P> config = controllerConfiguration(reconciler, annotation);
101+
102+
final var workflowAnnotation = reconciler.getClass().getAnnotation(
103+
io.javaoperatorsdk.operator.api.reconciler.Workflow.class);
104+
if (workflowAnnotation != null) {
105+
final var specs = dependentResources(workflowAnnotation, config);
106+
WorkflowSpec workflowSpec = new WorkflowSpec() {
107+
@Override
108+
public List<DependentResourceSpec> getDependentResourceSpecs() {
109+
return specs;
110+
}
111+
112+
@Override
113+
public boolean isExplicitInvocation() {
114+
return workflowAnnotation.explicitInvocation();
115+
}
116+
117+
@Override
118+
public boolean handleExceptionsInReconciler() {
119+
return workflowAnnotation.handleExceptionsInReconciler();
120+
}
121+
122+
};
123+
config.setWorkflowSpec(workflowSpec);
109124
}
125+
126+
return config;
127+
}
128+
129+
@SuppressWarnings({"unchecked"})
130+
private <P extends HasMetadata> ResolvedControllerConfiguration<P> controllerConfiguration(
131+
Reconciler<P> reconciler,
132+
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration annotation) {
110133
Class<Reconciler<P>> reconcilerClass = (Class<Reconciler<P>>) reconciler.getClass();
111134
final var resourceClass = getResourceClassResolver().getResourceClass(reconcilerClass);
112135

113136
final var name = ReconcilerUtils.getNameFor(reconciler);
114-
final var generationAware = valueOrDefault(
137+
final var generationAware = valueOrDefaultFromAnnotation(
115138
annotation,
116139
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::generationAwareEventProcessing,
117-
true);
140+
"generationAwareEventProcessing");
118141
final var associatedReconcilerClass =
119142
ResolvedControllerConfiguration.getAssociatedReconcilerClassName(reconciler.getClass());
120143

121144
final var context = Utils.contextFor(name);
122-
final Class<? extends Retry> retryClass = annotation.retry();
145+
final Class<? extends Retry> retryClass =
146+
valueOrDefaultFromAnnotation(annotation,
147+
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::retry,
148+
"retry");
123149
final var retry = Utils.instantiateAndConfigureIfNeeded(retryClass, Retry.class,
124150
context, configuratorFor(Retry.class, reconciler));
125151

126-
final Class<? extends RateLimiter> rateLimiterClass = annotation.rateLimiter();
152+
153+
final Class<? extends RateLimiter> rateLimiterClass = valueOrDefaultFromAnnotation(annotation,
154+
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::rateLimiter,
155+
"rateLimiter");
127156
final var rateLimiter = Utils.instantiateAndConfigureIfNeeded(rateLimiterClass,
128157
RateLimiter.class, context, configuratorFor(RateLimiter.class, reconciler));
129158

130-
final var reconciliationInterval = annotation.maxReconciliationInterval();
159+
final var reconciliationInterval = valueOrDefaultFromAnnotation(annotation,
160+
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::maxReconciliationInterval,
161+
"maxReconciliationInterval");
131162
long interval = -1;
132163
TimeUnit timeUnit = null;
133164
if (reconciliationInterval != null && reconciliationInterval.interval() > 0) {
134165
interval = reconciliationInterval.interval();
135166
timeUnit = reconciliationInterval.timeUnit();
136167
}
137168

169+
var fieldManager = valueOrDefaultFromAnnotation(annotation,
170+
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::fieldManager,
171+
"fieldManager");
138172
final var dependentFieldManager =
139-
annotation.fieldManager().equals(CONTROLLER_NAME_AS_FIELD_MANAGER) ? name
140-
: annotation.fieldManager();
173+
fieldManager.equals(CONTROLLER_NAME_AS_FIELD_MANAGER) ? name
174+
: fieldManager;
141175

176+
var informerListLimitValue = valueOrDefaultFromAnnotation(annotation,
177+
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::informerListLimit,
178+
"informerListLimit");
142179
final var informerListLimit =
143-
annotation.informerListLimit() == Constants.NO_LONG_VALUE_SET ? null
144-
: annotation.informerListLimit();
180+
informerListLimitValue == Constants.NO_LONG_VALUE_SET ? null
181+
: informerListLimitValue;
145182

146-
final var config = new ResolvedControllerConfiguration<P>(
183+
return new ResolvedControllerConfiguration<P>(
147184
resourceClass, name, generationAware,
148185
associatedReconcilerClass, retry, rateLimiter,
149186
ResolvedControllerConfiguration.getMaxReconciliationInterval(interval, timeUnit),
150-
Utils.instantiate(annotation.onAddFilter(), OnAddFilter.class, context),
151-
Utils.instantiate(annotation.onUpdateFilter(), OnUpdateFilter.class, context),
152-
Utils.instantiate(annotation.genericFilter(), GenericFilter.class, context),
153-
Set.of(valueOrDefault(annotation,
187+
Utils.instantiate(valueOrDefaultFromAnnotation(annotation,
188+
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::onAddFilter,
189+
"onAddFilter"), OnAddFilter.class, context),
190+
Utils.instantiate(valueOrDefaultFromAnnotation(annotation,
191+
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::onUpdateFilter,
192+
"onUpdateFilter"), OnUpdateFilter.class, context),
193+
Utils.instantiate(valueOrDefaultFromAnnotation(annotation,
194+
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::genericFilter,
195+
"genericFilter"), GenericFilter.class, context),
196+
Set.of(valueOrDefaultFromAnnotation(annotation,
154197
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::namespaces,
155-
DEFAULT_NAMESPACES_SET.toArray(String[]::new))),
156-
valueOrDefault(annotation,
198+
"namespaces")),
199+
valueOrDefaultFromAnnotation(annotation,
157200
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::finalizerName,
158-
Constants.NO_VALUE_SET),
159-
valueOrDefault(annotation,
201+
"finalizerName"),
202+
valueOrDefaultFromAnnotation(annotation,
160203
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::labelSelector,
161-
Constants.NO_VALUE_SET),
204+
"labelSelector"),
162205
null,
163-
Utils.instantiate(annotation.itemStore(), ItemStore.class, context), dependentFieldManager,
206+
Utils.instantiate(
207+
valueOrDefaultFromAnnotation(annotation,
208+
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::itemStore,
209+
"itemStore"),
210+
ItemStore.class, context),
211+
dependentFieldManager,
164212
this, informerListLimit);
165-
166-
167-
final var workflowAnnotation = reconciler.getClass().getAnnotation(
168-
io.javaoperatorsdk.operator.api.reconciler.Workflow.class);
169-
if (workflowAnnotation != null) {
170-
final var specs = dependentResources(workflowAnnotation, config);
171-
WorkflowSpec workflowSpec = new WorkflowSpec() {
172-
@Override
173-
public List<DependentResourceSpec> getDependentResourceSpecs() {
174-
return specs;
175-
}
176-
177-
@Override
178-
public boolean isExplicitInvocation() {
179-
return workflowAnnotation.explicitInvocation();
180-
}
181-
182-
@Override
183-
public boolean handleExceptionsInReconciler() {
184-
return workflowAnnotation.handleExceptionsInReconciler();
185-
}
186-
187-
};
188-
config.setWorkflowSpec(workflowSpec);
189-
}
190-
191-
return config;
192213
}
193214

194215
@SuppressWarnings({"unchecked", "rawtypes"})
@@ -239,14 +260,20 @@ public boolean checkCRDAndValidateLocalModel() {
239260
return Utils.shouldCheckCRDAndValidateLocalModel();
240261
}
241262

242-
private static <T> T valueOrDefault(
263+
@SuppressWarnings("unchecked")
264+
private static <T> T valueOrDefaultFromAnnotation(
243265
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration controllerConfiguration,
244266
Function<io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration, T> mapper,
245-
T defaultValue) {
246-
if (controllerConfiguration == null) {
247-
return defaultValue;
248-
} else {
249-
return mapper.apply(controllerConfiguration);
267+
String defaultMethodName) {
268+
try {
269+
if (controllerConfiguration == null) {
270+
return (T) io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration.class
271+
.getDeclaredMethod(defaultMethodName).getDefaultValue();
272+
} else {
273+
return mapper.apply(controllerConfiguration);
274+
}
275+
} catch (NoSuchMethodException e) {
276+
throw new RuntimeException(e);
250277
}
251278
}
252279

operator-framework/src/test/java/io/javaoperatorsdk/operator/config/BaseConfigurationServiceTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@
1010
import java.util.Set;
1111
import java.util.concurrent.TimeUnit;
1212

13-
import org.junit.jupiter.api.Assertions;
1413
import org.junit.jupiter.api.Test;
1514

1615
import io.fabric8.kubernetes.api.model.ConfigMap;
1716
import io.fabric8.kubernetes.api.model.HasMetadata;
18-
import io.javaoperatorsdk.operator.OperatorException;
1917
import io.javaoperatorsdk.operator.api.config.AnnotationConfigurable;
2018
import io.javaoperatorsdk.operator.api.config.BaseConfigurationService;
2119
import io.javaoperatorsdk.operator.api.config.dependent.ConfigurationConverter;
@@ -121,9 +119,11 @@ void getDependentResources() {
121119
}
122120

123121
@Test
124-
void missingAnnotationThrowsException() {
122+
void missingAnnotationCreatesDefaultConfig() {
125123
final var reconciler = new MissingAnnotationReconciler();
126-
Assertions.assertThrows(OperatorException.class, () -> configFor(reconciler));
124+
var config = configFor(reconciler);
125+
// todo asserts
126+
127127
}
128128

129129
@SuppressWarnings("rawtypes")

0 commit comments

Comments
 (0)