@@ -149,6 +149,212 @@ For more information on how to use this feature, we recommend looking at how thi
149149` KubernetesDependentResource ` in the core framework, ` SchemaDependentResource ` in the samples or ` CustomAnnotationDep `
150150in the ` BaseConfigurationServiceTest ` test class.
151151
152- ## EventSource-level configuration
152+ ## Loading Configuration from External Sources
153+
154+ JOSDK ships a ` ConfigLoader ` that bridges any key-value configuration source to the operator and
155+ controller configuration APIs. This lets you drive operator behaviour from environment variables,
156+ system properties, YAML files, or any config library (MicroProfile Config, SmallRye Config,
157+ Spring Environment, etc.) without writing glue code by hand.
158+
159+ ### Architecture
160+
161+ The system is built around two thin abstractions:
162+
163+ - ** [ ` ConfigProvider ` ] ( https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/loader/ConfigProvider.java ) **
164+ — a single-method interface that resolves a typed value for a dot-separated key:
165+
166+ ``` java
167+ public interface ConfigProvider {
168+ <T > Optional<T > getValue (String key , Class<T > type );
169+ }
170+ ```
171+
172+ - ** [ ` ConfigLoader ` ] ( https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/loader/ConfigLoader.java ) **
173+ — reads all known JOSDK keys from a ` ConfigProvider ` and returns
174+ ` Consumer<ConfigurationServiceOverrider> ` / ` Consumer<ControllerConfigurationOverrider<R>> `
175+ values that you pass directly to the ` Operator ` constructor or ` operator.register() ` .
176+
177+ The default ` ConfigLoader ` (no-arg constructor) stacks environment variables over system
178+ properties: environment variables win, system properties are the fallback.
179+
180+ ``` java
181+ // uses env vars + system properties out of the box
182+ Operator operator = new Operator (ConfigLoader . getDefault(). applyConfigs());
183+ ```
184+
185+ ### Built-in Providers
186+
187+ | Provider | Source | Key mapping |
188+ | ---| ---| ---|
189+ | ` EnvVarConfigProvider ` | ` System.getenv() ` | dots and hyphens → underscores, upper-cased (` josdk.check-crd ` → ` JOSDK_CHECK_CRD ` ) |
190+ | ` PropertiesConfigProvider ` | ` java.util.Properties ` or ` .properties ` file | key used as-is; use ` PropertiesConfigProvider.systemProperties() ` to read Java system properties |
191+ | ` YamlConfigProvider ` | YAML file | dot-separated key traverses nested mappings |
192+ | ` AgregatePrirityListConfigProvider ` | ordered list of providers | first non-empty result wins |
193+
194+ All string-based providers convert values to the target type automatically.
195+ Supported types: ` String ` , ` Boolean ` , ` Integer ` , ` Long ` , ` Double ` , ` Duration ` (ISO-8601, e.g. ` PT30S ` ).
196+
197+ ### Plugging in Any Config Library
198+
199+ ` ConfigProvider ` is a single-method interface, so adapting any config library takes only a few
200+ lines. As an example, here is an adapter for
201+ [ SmallRye Config] ( https://smallrye.io/smallrye-config/ ) :
202+
203+ ``` java
204+ public class SmallRyeConfigProvider implements ConfigProvider {
205+
206+ private final SmallRyeConfig config;
207+
208+ public SmallRyeConfigProvider (SmallRyeConfig config ) {
209+ this . config = config;
210+ }
211+
212+ @Override
213+ public <T > Optional<T > getValue (String key , Class<T > type ) {
214+ return config. getOptionalValue(key, type);
215+ }
216+ }
217+ ```
218+
219+ The same pattern applies to MicroProfile Config, Spring ` Environment ` , Apache Commons
220+ Configuration, or any other library that can look up typed values by string key.
221+
222+ ### Wiring Everything Together
223+
224+ Pass the ` ConfigLoader ` results when constructing the operator and registering reconcilers:
225+
226+ ``` java
227+ // Load operator-wide config from a YAML file via SmallRye Config
228+ URL configUrl = MyOperator . class. getResource(" /application.yaml" );
229+ var configLoader = new ConfigLoader (
230+ new SmallRyeConfigProvider (
231+ new SmallRyeConfigBuilder ()
232+ .withSources(new YamlConfigSource (configUrl))
233+ .build()));
234+
235+ // applyConfigs() → Consumer<ConfigurationServiceOverrider>
236+ Operator operator = new Operator (configLoader. applyConfigs());
237+
238+ // applyControllerConfigs(name) → Consumer<ControllerConfigurationOverrider<R>>
239+ operator. register(new MyReconciler (),
240+ configLoader. applyControllerConfigs(MyReconciler . NAME ));
241+ ```
242+
243+ Only keys that are actually present in the source are applied; everything else retains its
244+ programmatic or annotation-based default.
245+
246+ You can also compose multiple sources with explicit priority using
247+ ` AgregatePrirityListConfigProvider ` :
248+
249+ ``` java
250+ var configLoader = new ConfigLoader (
251+ new AgregatePrirityListConfigProvider (List . of(
252+ new EnvVarConfigProvider (), // highest priority
253+ PropertiesConfigProvider . systemProperties(),
254+ new YamlConfigProvider (Path . of(" config/operator.yaml" )) // lowest priority
255+ )));
256+ ```
257+
258+ ### Operator-Level Configuration Keys
259+
260+ All operator-level keys are prefixed with ` josdk. ` .
261+
262+ #### General
263+
264+ | Key | Type | Description |
265+ | ---| ---| ---|
266+ | ` josdk.check-crd ` | ` Boolean ` | Validate CRDs against local model on startup |
267+ | ` josdk.close-client-on-stop ` | ` Boolean ` | Close the Kubernetes client when the operator stops |
268+ | ` josdk.use-ssa-to-patch-primary-resource ` | ` Boolean ` | Use Server-Side Apply to patch the primary resource |
269+ | ` josdk.clone-secondary-resources-when-getting-from-cache ` | ` Boolean ` | Clone secondary resources on cache reads |
270+
271+ #### Reconciliation
272+
273+ | Key | Type | Description |
274+ | ---| ---| ---|
275+ | ` josdk.reconciliation.concurrent-threads ` | ` Integer ` | Thread pool size for reconciliation |
276+ | ` josdk.reconciliation.termination-timeout ` | ` Duration ` | How long to wait for in-flight reconciliations to finish on shutdown |
277+
278+ #### Workflow
279+
280+ | Key | Type | Description |
281+ | ---| ---| ---|
282+ | ` josdk.workflow.executor-threads ` | ` Integer ` | Thread pool size for workflow execution |
283+
284+ #### Informer
285+
286+ | Key | Type | Description |
287+ | ---| ---| ---|
288+ | ` josdk.informer.cache-sync-timeout ` | ` Duration ` | Timeout for the initial informer cache sync |
289+ | ` josdk.informer.stop-on-error-during-startup ` | ` Boolean ` | Stop the operator if an informer fails to start |
290+
291+ #### Dependent Resources
292+
293+ | Key | Type | Description |
294+ | ---| ---| ---|
295+ | ` josdk.dependent-resources.ssa-based-create-update-match ` | ` Boolean ` | Use SSA-based matching for dependent resource create/update |
296+
297+ #### Leader Election
298+
299+ Leader election is activated when at least one ` josdk.leader-election.* ` key is present.
300+ ` josdk.leader-election.lease-name ` is required when any other leader-election key is set.
301+ Setting ` josdk.leader-election.enabled=false ` suppresses leader election even if other keys are
302+ present.
303+
304+ | Key | Type | Description |
305+ | ---| ---| ---|
306+ | ` josdk.leader-election.enabled ` | ` Boolean ` | Explicitly enable (` true ` ) or disable (` false ` ) leader election |
307+ | ` josdk.leader-election.lease-name ` | ` String ` | ** Required.** Name of the Kubernetes Lease object used for leader election |
308+ | ` josdk.leader-election.lease-namespace ` | ` String ` | Namespace for the Lease object (defaults to the operator's namespace) |
309+ | ` josdk.leader-election.identity ` | ` String ` | Unique identity for this instance; defaults to the pod name |
310+ | ` josdk.leader-election.lease-duration ` | ` Duration ` | How long a lease is valid (default ` PT15S ` ) |
311+ | ` josdk.leader-election.renew-deadline ` | ` Duration ` | How long the leader tries to renew before giving up (default ` PT10S ` ) |
312+ | ` josdk.leader-election.retry-period ` | ` Duration ` | How often a candidate polls while waiting to become leader (default ` PT2S ` ) |
313+
314+ ### Controller-Level Configuration Keys
315+
316+ All controller-level keys are prefixed with ` josdk.controller.<controller-name>. ` , where
317+ ` <controller-name> ` is the value returned by the reconciler's name (typically set via
318+ ` @ControllerConfiguration(name = "...") ` ).
319+
320+ #### General
321+
322+ | Key | Type | Description |
323+ | ---| ---| ---|
324+ | ` josdk.controller.<name>.finalizer ` | ` String ` | Finalizer string added to managed resources |
325+ | ` josdk.controller.<name>.generation-aware ` | ` Boolean ` | Skip reconciliation when the resource generation has not changed |
326+ | ` josdk.controller.<name>.label-selector ` | ` String ` | Label selector to filter watched resources |
327+ | ` josdk.controller.<name>.max-reconciliation-interval ` | ` Duration ` | Maximum interval between reconciliations even without events |
328+ | ` josdk.controller.<name>.field-manager ` | ` String ` | Field manager name used for SSA operations |
329+ | ` josdk.controller.<name>.trigger-reconciler-on-all-events ` | ` Boolean ` | Trigger reconciliation on every event, not only meaningful changes |
330+
331+ #### Informer
332+
333+ | Key | Type | Description |
334+ | ---| ---| ---|
335+ | ` josdk.controller.<name>.informer.label-selector ` | ` String ` | Label selector for the primary resource informer (alias for ` label-selector ` ) |
336+ | ` josdk.controller.<name>.informer.list-limit ` | ` Long ` | Page size for paginated informer list requests; omit for no pagination |
337+
338+ #### Retry
339+
340+ If any ` retry.* ` key is present, a ` GenericRetry ` is configured starting from the
341+ [ default limited exponential retry] ( https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java ) .
342+ Only explicitly set keys override the defaults.
343+
344+ | Key | Type | Description |
345+ | ---| ---| ---|
346+ | ` josdk.controller.<name>.retry.max-attempts ` | ` Integer ` | Maximum number of retry attempts |
347+ | ` josdk.controller.<name>.retry.initial-interval ` | ` Long ` (ms) | Initial backoff interval in milliseconds |
348+ | ` josdk.controller.<name>.retry.interval-multiplier ` | ` Double ` | Exponential backoff multiplier |
349+ | ` josdk.controller.<name>.retry.max-interval ` | ` Long ` (ms) | Maximum backoff interval in milliseconds |
350+
351+ #### Rate Limiter
352+
353+ The rate limiter is only activated when ` rate-limiter.limit-for-period ` is present and has a
354+ positive value. ` rate-limiter.refresh-period ` is optional and falls back to the default of 10 s.
355+
356+ | Key | Type | Description |
357+ | ---| ---| ---|
358+ | ` josdk.controller.<name>.rate-limiter.limit-for-period ` | ` Integer ` | Maximum number of reconciliations allowed per refresh period. Must be positive to activate the limiter |
359+ | ` josdk.controller.<name>.rate-limiter.refresh-period ` | ` Duration ` | Window over which the limit is counted (default ` PT10S ` ) |
153360
154- TODO
0 commit comments