Skip to content
Open
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
210 changes: 208 additions & 2 deletions docs/content/en/docs/documentation/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,212 @@ For more information on how to use this feature, we recommend looking at how thi
`KubernetesDependentResource` in the core framework, `SchemaDependentResource` in the samples or `CustomAnnotationDep`
in the `BaseConfigurationServiceTest` test class.

## EventSource-level configuration
## Loading Configuration from External Sources

JOSDK ships a `ConfigLoader` that bridges any key-value configuration source to the operator and
controller configuration APIs. This lets you drive operator behaviour from environment variables,
system properties, YAML files, or any config library (MicroProfile Config, SmallRye Config,
Spring Environment, etc.) without writing glue code by hand.

### Architecture

The system is built around two thin abstractions:

- **[`ConfigProvider`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/loader/ConfigProvider.java)**
— a single-method interface that resolves a typed value for a dot-separated key:

```java
public interface ConfigProvider {
<T> Optional<T> getValue(String key, Class<T> type);
}
```

- **[`ConfigLoader`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/loader/ConfigLoader.java)**
— reads all known JOSDK keys from a `ConfigProvider` and returns
`Consumer<ConfigurationServiceOverrider>` / `Consumer<ControllerConfigurationOverrider<R>>`
values that you pass directly to the `Operator` constructor or `operator.register()`.

The default `ConfigLoader` (no-arg constructor) stacks environment variables over system
properties: environment variables win, system properties are the fallback.

```java
// uses env vars + system properties out of the box
Operator operator = new Operator(ConfigLoader.getDefault().applyConfigs());
```

### Built-in Providers

| Provider | Source | Key mapping |
|---|---|---|
| `EnvVarConfigProvider` | `System.getenv()` | dots and hyphens → underscores, upper-cased (`josdk.check-crd` → `JOSDK_CHECK_CRD`) |
| `PropertiesConfigProvider` | `java.util.Properties` or `.properties` file | key used as-is; use `PropertiesConfigProvider.systemProperties()` to read Java system properties |
| `YamlConfigProvider` | YAML file | dot-separated key traverses nested mappings |
| `AgregatePriorityListConfigProvider` | ordered list of providers | first non-empty result wins |

All string-based providers convert values to the target type automatically.
Supported types: `String`, `Boolean`, `Integer`, `Long`, `Double`, `Duration` (ISO-8601, e.g. `PT30S`).

### Plugging in Any Config Library

`ConfigProvider` is a single-method interface, so adapting any config library takes only a few
lines. As an example, here is an adapter for
[SmallRye Config](https://smallrye.io/smallrye-config/):

```java
public class SmallRyeConfigProvider implements ConfigProvider {

private final SmallRyeConfig config;

public SmallRyeConfigProvider(SmallRyeConfig config) {
this.config = config;
}

@Override
public <T> Optional<T> getValue(String key, Class<T> type) {
return config.getOptionalValue(key, type);
}
}
```

The same pattern applies to MicroProfile Config, Spring `Environment`, Apache Commons
Configuration, or any other library that can look up typed values by string key.

### Wiring Everything Together

Pass the `ConfigLoader` results when constructing the operator and registering reconcilers:

```java
// Load operator-wide config from a YAML file via SmallRye Config
URL configUrl = MyOperator.class.getResource("/application.yaml");
var configLoader = new ConfigLoader(
new SmallRyeConfigProvider(
new SmallRyeConfigBuilder()
.withSources(new YamlConfigSource(configUrl))
.build()));

// applyConfigs() → Consumer<ConfigurationServiceOverrider>
Operator operator = new Operator(configLoader.applyConfigs());

// applyControllerConfigs(name) → Consumer<ControllerConfigurationOverrider<R>>
operator.register(new MyReconciler(),
configLoader.applyControllerConfigs(MyReconciler.NAME));
```

Only keys that are actually present in the source are applied; everything else retains its
programmatic or annotation-based default.

You can also compose multiple sources with explicit priority using
`AgregatePriorityListConfigProvider`:

```java
var configLoader = new ConfigLoader(
new AgregatePriorityListConfigProvider(List.of(
new EnvVarConfigProvider(), // highest priority
PropertiesConfigProvider.systemProperties(),
new YamlConfigProvider(Path.of("config/operator.yaml")) // lowest priority
)));
```

### Operator-Level Configuration Keys

All operator-level keys are prefixed with `josdk.`.

#### General

| Key | Type | Description |
|---|---|---|
| `josdk.check-crd` | `Boolean` | Validate CRDs against local model on startup |
| `josdk.close-client-on-stop` | `Boolean` | Close the Kubernetes client when the operator stops |
| `josdk.use-ssa-to-patch-primary-resource` | `Boolean` | Use Server-Side Apply to patch the primary resource |
| `josdk.clone-secondary-resources-when-getting-from-cache` | `Boolean` | Clone secondary resources on cache reads |

#### Reconciliation

| Key | Type | Description |
|---|---|---|
| `josdk.reconciliation.concurrent-threads` | `Integer` | Thread pool size for reconciliation |
| `josdk.reconciliation.termination-timeout` | `Duration` | How long to wait for in-flight reconciliations to finish on shutdown |

#### Workflow

| Key | Type | Description |
|---|---|---|
| `josdk.workflow.executor-threads` | `Integer` | Thread pool size for workflow execution |

#### Informer

| Key | Type | Description |
|---|---|---|
| `josdk.informer.cache-sync-timeout` | `Duration` | Timeout for the initial informer cache sync |
| `josdk.informer.stop-on-error-during-startup` | `Boolean` | Stop the operator if an informer fails to start |

#### Dependent Resources

| Key | Type | Description |
|---|---|---|
| `josdk.dependent-resources.ssa-based-create-update-match` | `Boolean` | Use SSA-based matching for dependent resource create/update |

#### Leader Election

Leader election is activated when at least one `josdk.leader-election.*` key is present.
`josdk.leader-election.lease-name` is required when any other leader-election key is set.
Setting `josdk.leader-election.enabled=false` suppresses leader election even if other keys are
present.

| Key | Type | Description |
|---|---|---|
| `josdk.leader-election.enabled` | `Boolean` | Explicitly enable (`true`) or disable (`false`) leader election |
| `josdk.leader-election.lease-name` | `String` | **Required.** Name of the Kubernetes Lease object used for leader election |
| `josdk.leader-election.lease-namespace` | `String` | Namespace for the Lease object (defaults to the operator's namespace) |
| `josdk.leader-election.identity` | `String` | Unique identity for this instance; defaults to the pod name |
| `josdk.leader-election.lease-duration` | `Duration` | How long a lease is valid (default `PT15S`) |
| `josdk.leader-election.renew-deadline` | `Duration` | How long the leader tries to renew before giving up (default `PT10S`) |
| `josdk.leader-election.retry-period` | `Duration` | How often a candidate polls while waiting to become leader (default `PT2S`) |

### Controller-Level Configuration Keys

All controller-level keys are prefixed with `josdk.controller.<controller-name>.`, where
`<controller-name>` is the value returned by the reconciler's name (typically set via
`@ControllerConfiguration(name = "...")`).

#### General

| Key | Type | Description |
|---|---|---|
| `josdk.controller.<name>.finalizer` | `String` | Finalizer string added to managed resources |
| `josdk.controller.<name>.generation-aware` | `Boolean` | Skip reconciliation when the resource generation has not changed |
| `josdk.controller.<name>.label-selector` | `String` | Label selector to filter watched resources |
| `josdk.controller.<name>.max-reconciliation-interval` | `Duration` | Maximum interval between reconciliations even without events |
| `josdk.controller.<name>.field-manager` | `String` | Field manager name used for SSA operations |
| `josdk.controller.<name>.trigger-reconciler-on-all-events` | `Boolean` | Trigger reconciliation on every event, not only meaningful changes |

#### Informer

| Key | Type | Description |
|---|---|---|
| `josdk.controller.<name>.informer.label-selector` | `String` | Label selector for the primary resource informer (alias for `label-selector`) |
| `josdk.controller.<name>.informer.list-limit` | `Long` | Page size for paginated informer list requests; omit for no pagination |

#### Retry

If any `retry.*` key is present, a `GenericRetry` is configured starting from the
[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).
Only explicitly set keys override the defaults.

| Key | Type | Description |
|---|---|---|
| `josdk.controller.<name>.retry.max-attempts` | `Integer` | Maximum number of retry attempts |
| `josdk.controller.<name>.retry.initial-interval` | `Long` (ms) | Initial backoff interval in milliseconds |
| `josdk.controller.<name>.retry.interval-multiplier` | `Double` | Exponential backoff multiplier |
| `josdk.controller.<name>.retry.max-interval` | `Long` (ms) | Maximum backoff interval in milliseconds |

#### Rate Limiter

The rate limiter is only activated when `rate-limiter.limit-for-period` is present and has a
positive value. `rate-limiter.refresh-period` is optional and falls back to the default of 10 s.

| Key | Type | Description |
|---|---|---|
| `josdk.controller.<name>.rate-limiter.limit-for-period` | `Integer` | Maximum number of reconciliations allowed per refresh period. Must be positive to activate the limiter |
| `josdk.controller.<name>.rate-limiter.refresh-period` | `Duration` | Window over which the limit is counted (default `PT10S`) |

TODO
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright Java Operator SDK Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.javaoperatorsdk.operator.config.loader;

import java.util.function.BiConsumer;

/**
* Associates a configuration key and its expected type with the setter that should be called on an
* overrider when the {@link ConfigProvider} returns a value for that key.
*
* @param <O> the overrider type (e.g. {@code ConfigurationServiceOverrider})
* @param <T> the value type expected for this key
*/
public class ConfigBinding<O, T> {

private final String key;
private final Class<T> type;
private final BiConsumer<O, T> setter;

public ConfigBinding(String key, Class<T> type, BiConsumer<O, T> setter) {
this.key = key;
this.type = type;
this.setter = setter;
}

public String key() {
return key;
}

public Class<T> type() {
return type;
}

public BiConsumer<O, T> setter() {
return setter;
}
}
Loading