Skip to content

Commit a328e32

Browse files
authored
docs: blog about 5.2 release (#3063)
Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
1 parent c8d016a commit a328e32

File tree

1 file changed

+232
-0
lines changed

1 file changed

+232
-0
lines changed
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
---
2+
title: Version 5.2 Released!
3+
date: 2025-11-25
4+
author: >-
5+
[Attila Mészáros](https://github.com/csviri)
6+
---
7+
8+
We're pleased to announce the release of Java Operator SDK v5.2! This minor version brings several powerful new features
9+
and improvements that enhance the framework's capabilities for building Kubernetes operators. This release focuses on
10+
flexibility, external resource management, and advanced reconciliation patterns.
11+
12+
## Key Features
13+
14+
### ResourceIDMapper for External Resources
15+
16+
One of the most significant improvements in 5.2 is the introduction of a unified approach to working with custom ID types
17+
across the framework through [`ResourceIDMapper`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDMapper.java)
18+
and [`ResourceIDProvider`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/ResourceIDProvider.java).
19+
20+
Previously, when working with external resources (non-Kubernetes resources), the framework assumed resource IDs could always
21+
be represented as strings. This limitation made it challenging to work with external systems that use complex ID types.
22+
23+
Now, you can define custom ID types for your external resources by implementing the `ResourceIDProvider` interface:
24+
25+
```java
26+
public class MyExternalResource implements ResourceIDProvider<MyCustomID> {
27+
@Override
28+
public MyCustomID getResourceID() {
29+
return new MyCustomID(this.id);
30+
}
31+
}
32+
```
33+
34+
This capability is integrated across multiple components:
35+
- `ExternalResourceCachingEventSource`
36+
- `ExternalBulkDependentResource`
37+
- `AbstractExternalDependentResource` and its subclasses
38+
39+
If you cannot modify the external resource class (e.g., it's generated or final), you can provide a custom
40+
`ResourceIDMapper` to the components above.
41+
42+
See the [migration guide](/docs/migration/v5-2-migration) for detailed migration instructions.
43+
44+
### Trigger Reconciliation on All Events
45+
46+
Version 5.2 introduces a new execution mode that provides finer control over when reconciliation occurs. By setting
47+
[`triggerReconcilerOnAllEvent`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java)
48+
to `true`, your `reconcile` method will be called for **every** event, including `Delete` events.
49+
50+
This is particularly useful when:
51+
- Only some primary resources need finalizers (e.g., some resources create external resources, others don't)
52+
- You maintain custom in-memory caches that need cleanup without using finalizers
53+
- You need fine-grained control over resource lifecycle
54+
55+
When enabled:
56+
- The `reconcile` method receives the last known state even if the resource is deleted
57+
- Check deletion status using `Context.isPrimaryResourceDeleted()`
58+
- Retry, rate limiting, and rescheduling work normally
59+
- You manage finalizers explicitly using `PrimaryUpdateAndCacheUtils`
60+
61+
Example:
62+
63+
```java
64+
@ControllerConfiguration(triggerReconcilerOnAllEvent = true)
65+
public class MyReconciler implements Reconciler<MyResource> {
66+
67+
@Override
68+
public UpdateControl<MyResource> reconcile(MyResource resource, Context<MyResource> context) {
69+
if (context.isPrimaryResourceDeleted()) {
70+
// Handle deletion
71+
cleanupCache(resource);
72+
return UpdateControl.noUpdate();
73+
}
74+
// Normal reconciliation
75+
return UpdateControl.patchStatus(resource);
76+
}
77+
}
78+
```
79+
80+
See the detailed [documentation](/docs/documentation/reconciler#trigger-reconciliation-for-all-events) and
81+
[integration test](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/triggerallevent/finalizerhandling).
82+
83+
### Expectation Pattern Support (Experimental)
84+
85+
The framework now provides built-in support for the [expectations pattern](https://ahmet.im/blog/controller-pitfalls/#expectations-pattern),
86+
a common Kubernetes controller design pattern that ensures secondary resources are in an expected state before proceeding.
87+
88+
The expectation pattern helps avoid race conditions and ensures your controller makes decisions based on the most current
89+
state of your resources. The implementation is available in the
90+
[`io.javaoperatorsdk.operator.processing.expectation`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/expectation/)
91+
package.
92+
93+
Example usage:
94+
95+
```java
96+
public class MyReconciler implements Reconciler<MyResource> {
97+
98+
private final ExpectationManager<MyResource> expectationManager = new ExpectationManager<>();
99+
100+
@Override
101+
public UpdateControl<MyResource> reconcile(MyResource primary, Context<MyResource> context) {
102+
103+
// Exit early if expectation is not yet fulfilled or timed out
104+
if (expectationManager.ongoingExpectationPresent(primary, context)) {
105+
return UpdateControl.noUpdate();
106+
}
107+
108+
var deployment = context.getSecondaryResource(Deployment.class);
109+
if (deployment.isEmpty()) {
110+
createDeployment(primary, context);
111+
expectationManager.setExpectation(
112+
primary, Duration.ofSeconds(30), deploymentReadyExpectation(context));
113+
return UpdateControl.noUpdate();
114+
}
115+
116+
// Check if expectation is fulfilled
117+
var result = expectationManager.checkExpectation("deploymentReady", primary, context);
118+
if (result.isFulfilled()) {
119+
return updateStatusReady(primary);
120+
} else if (result.isTimedOut()) {
121+
return updateStatusTimeout(primary);
122+
}
123+
124+
return UpdateControl.noUpdate();
125+
}
126+
}
127+
```
128+
129+
This feature is marked as `@Experimental` as we gather feedback and may refine the API based on user experience. Future
130+
versions may integrate this pattern directly into Dependent Resources and Workflows.
131+
132+
See the [documentation](/docs/documentation/reconciler#expectations) and
133+
[integration test](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/expectation/ExpectationReconciler.java).
134+
135+
### Field Selectors for InformerEventSource
136+
137+
You can now use field selectors when configuring `InformerEventSource`, allowing you to filter resources at the server
138+
side before they're cached locally. This reduces memory usage and network traffic by only watching resources that match
139+
your criteria.
140+
141+
Field selectors work similarly to label selectors but filter on resource fields like `metadata.name` or `status.phase`:
142+
143+
```java
144+
@Informer(
145+
fieldSelector = @FieldSelector(
146+
fields = @Field(key = "status.phase", value = "Running")
147+
)
148+
)
149+
```
150+
151+
This is particularly useful when:
152+
- You only care about resources in specific states
153+
- You want to reduce the memory footprint of your operator
154+
- You're watching cluster-scoped resources and only need a subset
155+
156+
See the [integration test](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/fieldselector/FieldSelectorIT.java)
157+
for examples.
158+
159+
### AggregatedMetrics for Multiple Metrics Providers
160+
161+
The new `AggregatedMetrics` class implements the composite pattern, allowing you to combine multiple metrics
162+
implementations. This is useful when you need to send metrics to different monitoring systems simultaneously.
163+
164+
```java
165+
// Create individual metrics instances
166+
Metrics micrometerMetrics = MicrometerMetrics.withoutPerResourceMetrics(registry);
167+
Metrics customMetrics = new MyCustomMetrics();
168+
Metrics loggingMetrics = new LoggingMetrics();
169+
170+
// Combine them into a single aggregated instance
171+
Metrics aggregatedMetrics = new AggregatedMetrics(List.of(
172+
micrometerMetrics,
173+
customMetrics,
174+
loggingMetrics
175+
));
176+
177+
// Use with your operator
178+
Operator operator = new Operator(client, o -> o.withMetrics(aggregatedMetrics));
179+
```
180+
181+
This enables hybrid monitoring strategies, such as sending metrics to both Prometheus and a custom logging system.
182+
183+
See the [observability documentation](/docs/documentation/observability#aggregated-metrics) for more details.
184+
185+
## Additional Improvements
186+
187+
### GenericRetry Enhancements
188+
189+
- `GenericRetry` no longer provides a mutable singleton instance, improving thread safety
190+
- Configurable duration for initial retry interval
191+
192+
### Test Infrastructure Improvements
193+
194+
- Ability to override test infrastructure Kubernetes client separately, providing more flexibility in testing scenarios
195+
196+
### Fabric8 Client Update
197+
198+
Updated to Fabric8 Kubernetes Client 7.4.0, bringing the latest features and bug fixes from the client library.
199+
200+
## Experimental Annotations
201+
202+
Starting with this release, new features marked as experimental will be annotated with `@Experimental`. This annotation
203+
indicates that while we intend to support the feature, the API may evolve based on user feedback.
204+
205+
## Migration Notes
206+
207+
For most users, upgrading to 5.2 should be straightforward. The main breaking change involves the introduction of
208+
`ResourceIDMapper` for external resources. If you're using external dependent resources or bulk dependents with custom
209+
ID types, please refer to the [migration guide](/docs/migration/v5-2-migration).
210+
211+
## Getting Started
212+
213+
Update your dependency to version 5.2.0:
214+
215+
```xml
216+
<dependency>
217+
<groupId>io.javaoperatorsdk</groupId>
218+
<artifactId>operator-framework</artifactId>
219+
<version>5.2.0</version>
220+
</dependency>
221+
```
222+
223+
## All Changes
224+
225+
You can see all changes in the [comparison view](https://github.com/operator-framework/java-operator-sdk/compare/v5.1.5...v5.2.0).
226+
227+
## Feedback
228+
229+
As always, we welcome your feedback! Please report issues or suggest improvements on our
230+
[GitHub repository](https://github.com/operator-framework/java-operator-sdk/issues).
231+
232+
Happy operator building! 🚀

0 commit comments

Comments
 (0)