Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
02dadab
improve: add license headers to source files (#2980)
csviri Oct 9, 2025
319355d
chore: version to 5.3.0-SNAPSHOT
csviri Oct 14, 2025
eb4b21b
Annotation removal using locking (#3015)
shawkins Oct 30, 2025
f155074
improve: complete comparable resource version configs (#3027)
csviri Nov 13, 2025
678eafe
improve: run pr-s checks for v5.3 (#3042)
csviri Nov 17, 2025
eb205ac
fix: rebase on main after release
csviri Dec 1, 2025
bf1b996
fix(javadoc): invalid method ref blocks snapshot release (#3076)
csviri Dec 1, 2025
ecd9988
feat: record desired state in Context (#3082)
metacosm Dec 3, 2025
64c60e7
improve: rename junit5 module to junit (#3081)
csviri Dec 4, 2025
9a4ad30
fix: delete empty files result of rebase on main (#3093)
csviri Dec 15, 2025
fa6049b
feat: ReconcileUtils for strongly consistent updates (#3106)
csviri Jan 15, 2026
2b4795f
Event filtering now records resource action and previous resource (#3…
csviri Jan 21, 2026
120a28b
improve: facelift samples to use ReconcileUtils (#3135)
csviri Jan 27, 2026
4d384fd
improve: move compare resource version methods to internal utils (#3137)
csviri Jan 28, 2026
a28d7b4
feat: move ReconcileUtils methods to ResourceOperations accessible fr…
csviri Feb 2, 2026
59da824
improve: KubernetesDependentResource uses resource operations directl…
csviri Feb 2, 2026
9faeff4
feat: provide de-duplicated secondary resources stream on Context (#3…
metacosm Feb 3, 2026
bf394a1
refactor: avoid creating intermediate collections when unneeded (#3156)
metacosm Feb 5, 2026
1e09e29
improve: event filtering algorithm for multiple parallel updates (#3155)
csviri Feb 6, 2026
a33dbf7
improve: prepare for removal of exitOnStopLeading from public API (#3…
metacosm Feb 6, 2026
4d43730
fix: typo (#3173)
metacosm Feb 19, 2026
76614e1
fix: incorrect logic by introducing createOrUpdate method (#3172)
metacosm Feb 19, 2026
d078452
chore: set next version to 999-SNAPSHOT (#3180)
metacosm Feb 23, 2026
8b768ee
feat: allow to skip namespace deletion in junit extension (#3178)
csviri Feb 23, 2026
1c24e0d
improve: logging for resource filter cache (#3167)
csviri Feb 23, 2026
2226239
fix: unify how resource information is added, prevent NPEs (#3185)
metacosm Feb 25, 2026
8144ddf
feat: emit MDCUtils.NO_NAMESPACE value when namespace is null (#3186)
metacosm Feb 26, 2026
4868613
improve: do not close infra client if same as client (#3187)
csviri Feb 26, 2026
0afd4da
feat: add MDC to workflow execution (#3188)
csviri Feb 28, 2026
4ada736
fix: concurrency issue with filtering and caching update (#3191)
csviri Mar 2, 2026
4453e4a
improve: deprecate redundant ManagedInformerEventSource.getCachedValu…
csviri Mar 2, 2026
b657909
docs: read-cache-after-write consistency and event filtering (#3193)
csviri Mar 2, 2026
76ec20a
fix: logging for flaky MySQLSchema E2E test (#3198)
csviri Mar 2, 2026
04f17bd
feat: configuration adapters (#3177)
csviri Mar 6, 2026
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
3 changes: 2 additions & 1 deletion .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ on:
paths-ignore:
- 'docs/**'
- 'adr/**'
branches: [ main, next ]
branches: [ main, next, v5.3 ]
push:
paths-ignore:
- 'docs/**'
- 'adr/**'
branches:
- main
- next
- v5.3

jobs:
sample_operators_tests:
Expand Down
2 changes: 1 addition & 1 deletion bootstrapper-maven-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<parent>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>java-operator-sdk</artifactId>
<version>5.2.4-SNAPSHOT</version>
<version>999-SNAPSHOT</version>
</parent>

<artifactId>bootstrapper</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
</dependency>
<dependency>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>operator-framework-junit-5</artifactId>
<artifactId>operator-framework-junit</artifactId>
<version>${josdk.version}</version>
<scope>test</scope>
</dependency>
Expand Down
4 changes: 2 additions & 2 deletions caffeine-bounded-cache-support/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<parent>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>java-operator-sdk</artifactId>
<version>5.2.4-SNAPSHOT</version>
<version>999-SNAPSHOT</version>
</parent>

<artifactId>caffeine-bounded-cache-support</artifactId>
Expand All @@ -43,7 +43,7 @@
</dependency>
<dependency>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>operator-framework-junit-5</artifactId>
<artifactId>operator-framework-junit</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
Expand Down
9 changes: 9 additions & 0 deletions docs/content/en/blog/news/primary-cache-for-next-recon.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ author: >-
[Attila Mészáros](https://github.com/csviri) and [Chris Laprun](https://github.com/metacosm)
---

{{% alert title="Deprecated" %}}

Read-cache-after-write consistency feature replaces this functionality. (since version 5.3.0)

> It provides this functionality also for secondary resources and optimistic locking
is not required anymore. See [details here](./../../docs/documentation/reconciler.md#read-cache-after-write-consistency-and-event-filtering).
{{% /alert %}}


We recently released v5.1 of Java Operator SDK (JOSDK). One of the highlights of this release is related to a topic of
so-called
[allocated values](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#representing-allocated-values
Expand Down
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
29 changes: 29 additions & 0 deletions docs/content/en/docs/documentation/observability.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,35 @@ parts of reconciliation logic and during the execution of the controller:

For more information about MDC see this [link](https://www.baeldung.com/mdc-in-log4j-2-logback).

### MDC entries during event handling

Although, usually users might not require it in their day-to-day workflow, it is worth mentioning that
there are additional MDC entries managed for event handling. Typically, you might be interested in it
in your `SecondaryToPrimaryMapper` related logs.
For `InformerEventSource` and `ControllerEventSource` the following information is present:

| MDC Key | Value from Resource from the Event |
|:-----------------------------------------------|:-------------------------------------------------|
| `eventsource.event.resource.name` | `.metadata.name` |
| `eventsource.event.resource.uid` | `.metadata.uid` |
| `eventsource.event.resource.namespace` | `.metadata.namespace` |
| `eventsource.event.resource.kind` | resource kind |
| `eventsource.event.resource.resourceVersion` | `.metadata.resourceVersion` |
| `eventsource.event.action` | action name (e.g. `ADDED`, `UPDATED`, `DELETED`) |
| `eventsource.name` | name of the event source |

### Note on null values

If a resource doesn't provide values for one of the specified keys, the key will be omitted and not added to the MDC
context. There is, however, one notable exception: the resource's namespace, where, instead of omitting the key, we emit
the `MDCUtils.NO_NAMESPACE` value instead. This allows searching for resources without namespace (notably, clustered
resources) in the logs more easily.

### Disabling MDC support

MDC support is enabled by default. If you want to disable it, you can set the `JAVA_OPERATOR_SDK_USE_MDC` environment
variable to `false` when you start your operator.

## Metrics

JOSDK provides built-in support for metrics reporting on what is happening with your reconcilers in the form of
Expand Down
Loading