Skip to content

Commit 55eb340

Browse files
csvirixstefank
andcommitted
docs: release 5 blogpost
Signed-off-by: Attila Mészáros <a_meszaros@apple.com> Update docs/content/en/blog/releases/v5-release.md Co-authored-by: Martin Stefanko <xstefank122@gmail.com> Update docs/content/en/blog/releases/v5-release.md Co-authored-by: Martin Stefanko <xstefank122@gmail.com>
1 parent e8d44c4 commit 55eb340

File tree

1 file changed

+280
-0
lines changed

1 file changed

+280
-0
lines changed
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
---
2+
title: Version 5 Released!
3+
date: 2024-09-21
4+
---
5+
6+
We are excited to announce that Java Operator SDK v5 has been released. This significant effort contains
7+
various features and enhancements accumulated since the last major releases and required changes in our APIs.
8+
Within this post, we will go through all the main changes and help you upgrade to this new version, and provide
9+
a rationale behind the changes if necessary.
10+
11+
We will omit descriptions of changes that are trivial to update from the source code; feel free to contact
12+
us if you have some trouble with updates.
13+
14+
## Various Changes
15+
16+
- From this release, the minimal Java version is 17.
17+
- Various deprecated APIs are removed. The migration should be trivial.
18+
19+
## Naming changes
20+
21+
TODO add handy diff links here
22+
23+
## Changes in low-level APIs
24+
25+
### Server Side Apply
26+
27+
[Server Side Apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/) is now a first-class citizen in the framework and
28+
the default approach for patching the status resource. That means patching the resource or it's status through `UpdateControl` and adding
29+
the finalizer in the background.
30+
31+
Migration from a non-SSA based patching to an SSA based one can be problematic. Make sure you test the transition when you migrate from older version of the frameworks.
32+
To continue to use a non-SSA based on, set [ConfigurationService.useSSAToPatchPrimaryResource](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java#L462) to `false`.
33+
34+
See some identified problematic migration cases and how to handle them in [StatusPatchSSAMigrationIT](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchSSAMigrationIT.java).
35+
36+
TODO using new instance to update status always,
37+
38+
### Event Sources related changes
39+
40+
#### Multi-cluster support in InformerEventSource
41+
42+
`InformerEventSource` now supports watching remote clusters. You can simply pass an `KubernetesClient` that is
43+
initialized to connect to a different cluster where the controller runs. See [InformerEventSourceConfiguration.withKubernetesClient](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerEventSourceConfiguration.java)
44+
45+
Such an informer behaves exactly as a normal one. Obviously, owner references won't work, so you have to specify a `SecondaryToPrimaryMapper` (probably based on labels or annotations).
46+
47+
See related integration test [here](https://github.com/operator-framework/java-operator-sdk/tree/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster)
48+
49+
#### SecondaryToPrimaryMapper now checks resource types
50+
51+
The owner reference based mappers are now checking the type (`kind` and `apiVersion`) of the resource when resolving the mapping. This is important
52+
since a resource may have owner references to a different resource type with the same name.
53+
54+
See implementation details [here](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/Mappers.java#L74-L75)
55+
56+
#### InformerEventSource-related reactors
57+
58+
There are multiple smaller changes to `InformerEventSource` and related classes:
59+
60+
1. `InformerConfiguration` is renamed to [`InformerEventSourceConfiguration'](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerEventSourceConfiguration.java)
61+
2. `InformerEventSourceConfiguration` doesn't require `EventSourceContext` to be initialized anymore.
62+
63+
#### All EventSource is now a ResourceEventSource
64+
65+
The [`EventSource`](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSource.java) abstraction is now always aware of the resources and
66+
handles accessing (the cached) resources, filtering, and additional capabilities. Before v5, such capabilities were present only in a sub-class called `ResourceEventSource`,
67+
but we decided to merge and remove `ResourceEventSource` since this has a nice impact on other parts of the system in terms of architecture.
68+
69+
If you still need to create an `EventSource` that does only the triggering, see [TimerEventSource](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/timer/TimerEventSource.java) as an example.
70+
71+
#### Naming event sources
72+
73+
The `name` is now directly property of the [`EventSource`](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSource.java#L45).
74+
75+
This results in nicer internal structures. For example, if a DependentResource provides an EventSource, we have more options to set the name for it.
76+
77+
### ControllerConfiguration annotation related changes
78+
79+
You no longer have to annotate the reconciler with `@ControllerConfiguration` annotation.
80+
This annotation is (one) way to override the default properties of a controller.
81+
If the annotation is not present, the default values from the annotation are used.
82+
83+
PR: https://github.com/operator-framework/java-operator-sdk/pull/2203
84+
85+
In addition to that, the informer-related configurations are now extracted into
86+
a separate [`@Informer`](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/Informer.java) annotation within [`@ControllerConfiguration`](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java#L24). Hopefully this makes the underlying components more explicit
87+
and easier to understand. Note that the same `@Informer` annotation is used when configuring a managed `KubernetesDependentResource` with
88+
[`KubernetesDependent`](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java#L33) annotation.
89+
90+
91+
### EventSourceInitializer and ErrorStatusHandler are removed
92+
93+
Both the `EventSourceIntializer` and `ErrorStatusHandler` interfaces are removed, and their methods are moved directly
94+
under [`Reconciler`](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Reconciler.java#L30-L56).
95+
96+
If possible, we try to avoid such marker interfaces since it is hard to deduce related usage just by looking at the source code.
97+
You can now simply override those methods when implementing the `Reconciler` interface.
98+
99+
### Cloning accessing secondary resources
100+
101+
When accessing the secondary resources using [`Context.getSecondaryResource(s)(...)`](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java#L19-L29), the resources are no longer cloned by default, since
102+
cloning could have an impact on performance. Note that means that these POJOs should be used only for "read-only"; any changes
103+
are now made directly to the cached resource. This should be avoided since the same resource instance may be present for other reconciliation cycles and would
104+
no longer represent the state on the server.
105+
106+
If you want to still clone resource by default, set [ConfigurationService.cloneSecondaryResourcesWhenGettingFromCache](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java#L484) to `true`.
107+
108+
109+
### Remove automated observed generation handling
110+
111+
The automatic observed generation handling feature was removed since it is trivial to implement inside the reconciler, but it made
112+
the implementation much more complex, especially if the framework would have to support it both for served side apply and client side apply.
113+
114+
You can check a sample implementation how to do it manually in this [integration test](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/).
115+
116+
## Dependent Resource related changes
117+
118+
### ResourceDescriminator is removed and related changes
119+
120+
The primary reason `ResourceDiscriminator` was introduced is to cover the case when there are
121+
more dependent resources for that same type, so there was a need a generic mechanism to
122+
associate the resources from API served with the related dependent resource.
123+
This mechanism is now improved with a more lightweight approach, that made the `ResourceDiscriminator`
124+
obsolete.
125+
126+
As a replacement, the dependent resource will select the target resource based on the desired state.
127+
See the generic implementation in [`AbstractDependentResource`](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java#L135-L144).
128+
Calculating the desired state can be costly and might depend on other resources. For `KubernetesDependentResource`
129+
is usually enough to provide the name and namespace (if namespace-scoped) of the target resource, therefore
130+
in case the desired state is more heavy weight, in order to provide the ID of the target resource you might
131+
override [`ResourceID managedSecondaryResourceID()`](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java#L234-L244) method.
132+
133+
TODO add sample.
134+
135+
### Read-only bulk dependent resources
136+
137+
Read-only bulk dependent resources are now supported; this was a request from multiple users, but it required changes to the underlying APIs.
138+
Please check the documentation for further details.
139+
140+
See also the related [integration test](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly).
141+
142+
143+
### Multiple Dependents with Activation Condition
144+
145+
Until now, activation conditions had a limitation that only one condition was allowed for a specific resource type.
146+
For example, two ConfigMap dependent resources were not allowed, both with activation conditions. The underlying issue
147+
was with the informer registration process. When an activation condition is evaluated as "met" in the background,
148+
the informer is registered dynamically for the target resource type. However, we need to avoid registering multiple
149+
informers of the same kind. To prevent this the dependent resource must specify the [name of the informer](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/ConfigMapDependentResource2.java#L12).
150+
151+
See the complete example [here](https://github.com/operator-framework/java-operator-sdk/blob/1635c9ea338f8e89bacc547808d2b409de8734cf/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation).
152+
153+
154+
### The getSecondaryResource() is Activation condition aware
155+
156+
When there is an activation condition for a resource type, it might or might not be met based that
157+
an informer might be registered for the target kind. However, when calling `Context.getSecondaryResource`
158+
and its alternatives; it behaves differently if there is an Informer registered or not. Thus, normally
159+
throws an exception if there is no registered informer for the target type. For resources
160+
with activation condition, this might be confusing however. Therefore, if a dependent resource for a type with an activation
161+
condition is present, it always behaves as there is an Informer registered.
162+
163+
See related [issue](https://github.com/operator-framework/java-operator-sdk/issues/2198) for details.
164+
165+
## Workflow related changes
166+
167+
### Explicit workflow invocation
168+
169+
You can explicitly invoke managed workflows during reconciliation and/or cleanup, until now the execution always happened
170+
before the `reconcile(...)` (respectively before `cleanup(...)`). This mean you can do all kind of operations - typically validations -
171+
before executing the workflow. Sample:
172+
173+
```java
174+
@Workflow(explicitInvocation = true,
175+
dependents = @Dependent(type = ConfigMapDependent.class))
176+
@ControllerConfiguration
177+
public class WorkflowExplicitCleanupReconciler
178+
implements Reconciler<WorkflowExplicitCleanupCustomResource>,
179+
Cleaner<WorkflowExplicitCleanupCustomResource> {
180+
181+
@Override
182+
public UpdateControl<WorkflowExplicitCleanupCustomResource> reconcile(
183+
WorkflowExplicitCleanupCustomResource resource,
184+
Context<WorkflowExplicitCleanupCustomResource> context) {
185+
186+
context.managedWorkflowAndDependentResourceContext().reconcileManagedWorkflow();
187+
188+
return UpdateControl.noUpdate();
189+
}
190+
191+
@Override
192+
public DeleteControl cleanup(WorkflowExplicitCleanupCustomResource resource,
193+
Context<WorkflowExplicitCleanupCustomResource> context) {
194+
195+
context.managedWorkflowAndDependentResourceContext().cleanupManageWorkflow();
196+
// this can be checked
197+
// context.managedWorkflowAndDependentResourceContext().getWorkflowCleanupResult()
198+
return DeleteControl.defaultDelete();
199+
}
200+
}
201+
```
202+
203+
To turn on this mode of execution, set [`explicitInvocation`](https://github.com/operator-framework/java-operator-sdk/blob/664cb7109fe62f9822997d578ae7f57f17ef8c26/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Workflow.java#L26) flag to true in managed workflow definition.
204+
205+
See the following integration tests for [invocation](https://github.com/operator-framework/java-operator-sdk/blob/664cb7109fe62f9822997d578ae7f57f17ef8c26/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation) and [cleanup](https://github.com/operator-framework/java-operator-sdk/blob/664cb7109fe62f9822997d578ae7f57f17ef8c26/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup).
206+
207+
### Explicit exception handling
208+
209+
If an exception happens during reconciliation of a workflow, the framework automatically throws it further.
210+
You can now set [`handleExceptionsInReconciler`](https://github.com/operator-framework/java-operator-sdk/blob/664cb7109fe62f9822997d578ae7f57f17ef8c26/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Workflow.java#L40) to true for a workflow and check the thrown exceptions explicitly
211+
in the execution results.
212+
213+
```java
214+
@Workflow(handleExceptionsInReconciler = true,
215+
dependents = @Dependent(type = ConfigMapDependent.class))
216+
@ControllerConfiguration
217+
public class HandleWorkflowExceptionsInReconcilerReconciler
218+
implements Reconciler<HandleWorkflowExceptionsInReconcilerCustomResource>,
219+
Cleaner<HandleWorkflowExceptionsInReconcilerCustomResource> {
220+
221+
private volatile boolean errorsFoundInReconcilerResult = false;
222+
private volatile boolean errorsFoundInCleanupResult = false;
223+
224+
@Override
225+
public UpdateControl<HandleWorkflowExceptionsInReconcilerCustomResource> reconcile(
226+
HandleWorkflowExceptionsInReconcilerCustomResource resource,
227+
Context<HandleWorkflowExceptionsInReconcilerCustomResource> context) {
228+
229+
errorsFoundInReconcilerResult = context.managedWorkflowAndDependentResourceContext()
230+
.getWorkflowReconcileResult().erroredDependentsExist();
231+
232+
// check errors here:
233+
Map<DependentResource, Exception> errors = context.getErroredDependents();
234+
235+
return UpdateControl.noUpdate();
236+
}
237+
```
238+
239+
See integration test [here](https://github.com/operator-framework/java-operator-sdk/blob/664cb7109fe62f9822997d578ae7f57f17ef8c26/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling).
240+
241+
### @Workflow annotation
242+
243+
The managed workflow definition is now a separate `@Workflow` annotation; it is no longer part of `@ControllerConfiguration`.
244+
245+
See sample usage [here](https://github.com/operator-framework/java-operator-sdk/blob/664cb7109fe62f9822997d578ae7f57f17ef8c26/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageManagedDependentsReconciler.java#L14-L20)
246+
247+
### CRDPresentActivationCondition
248+
249+
Activation conditions are typically used to check if the cluster has specific capabilities (e.g., is cert-manager available).
250+
Such a check can be done by verifying if a particular custom resource definition (CRD) is present on the cluster. You
251+
can now use the generic [`CRDPresentActivationCondition`](https://github.com/operator-framework/java-operator-sdk/blob/664cb7109fe62f9822997d578ae7f57f17ef8c26/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/CRDPresentActivationCondition.java) for this
252+
purpose, it will check if the CRD of a target resource type of a dependent resource exists on the cluster.
253+
254+
See usage in integration test [here](https://github.com/operator-framework/java-operator-sdk/blob/refs/heads/next/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation).
255+
256+
## Experimental
257+
258+
### Check if the following reconciliation is imminent
259+
260+
You can now check if the subsequent reconciliation will happen right after the current one. In other words
261+
If we have already received a new event triggering the reconciliation for the actual resource.
262+
This information is available from the [context](https://github.com/operator-framework/java-operator-sdk/blob/664cb7109fe62f9822997d578ae7f57f17ef8c26/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java#L69).
263+
264+
Note that this could be useful, for example, in situations when a heavy task would be repeated in the follow-up reconciliation. In the current
265+
reconciliation, you can check this flag and return, don't do the heavy task twice. Note that this is a semi-experimental feature, so please let us know
266+
if you found this helpful.
267+
268+
```java
269+
@Override
270+
public UpdateControl<NextReconciliationImminentCustomResource> reconcile(MyCustomResource resource, Context<MyCustomResource> context) {
271+
272+
if (context.isNextReconciliationImminent()) {
273+
// your logic, maybe return?
274+
}
275+
}
276+
```
277+
278+
See related [integration test](https://github.com/operator-framework/java-operator-sdk/blob/664cb7109fe62f9822997d578ae7f57f17ef8c26/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent).
279+
280+

0 commit comments

Comments
 (0)