diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index 34bc6c7d0f..3fa5655cbc 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -22,7 +22,7 @@ jobs:
uses: actions/setup-java@v5
with:
distribution: temurin
- java-version: 25
+ java-version: 17
cache: 'maven'
- name: Check code format
run: |
diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml
index 7c9d1d24d0..3d4d2032f6 100644
--- a/.github/workflows/sonar.yml
+++ b/.github/workflows/sonar.yml
@@ -21,14 +21,14 @@ on:
jobs:
test:
runs-on: ubuntu-latest
- if: ${{ ( github.event_name == 'push' ) || ( github.event_name == 'pull_request' && github.event.pull_request.head.repo.owner.login == 'java-operator-sdk' ) }}
+ if: ${{ ( github.event_name == 'push' ) || ( github.event_name == 'pull_request' && github.event.pull_request.head.repo.owner.login == 'operator-framework' ) }}
steps:
- uses: actions/checkout@v6
- name: Set up Java and Maven
uses: actions/setup-java@v5
with:
distribution: temurin
- java-version: 25
+ java-version: 17
cache: 'maven'
- name: Cache SonarCloud packages
uses: actions/cache@v5
diff --git a/AGENTS.md b/AGENTS.md
index 62e19f3b69..2c311dac1d 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -25,7 +25,7 @@ Java Operator SDK is a production-ready framework for building Kubernetes Operat
java-operator-sdk/
├── operator-framework-core/ # Core reconciliation engine and API
├── operator-framework/ # Main operator framework implementation
-├── operator-framework-junit5/ # Testing utilities and extensions
+├── operator-framework-junit/ # Testing utilities and extensions
├── operator-framework-bom/ # Bill of Materials for dependency management
├── micrometer-support/ # Metrics integration
├── open-telemetry-support/ # Distributed tracing support
@@ -121,7 +121,7 @@ java-operator-sdk/
### Integration Tests
-- Use `LocallyRunOperatorExtension` or `OperatorExtension` from `operator-framework-junit5`
+- Use `LocallyRunOperatorExtension` or `OperatorExtension` from `operator-framework-junit`
- Test against real Kubernetes API (typically via test cluster like minikube or kind)
- Suffix with `IT` (e.g., `MyReconcilerIT`)
- Located in `src/test/java`
diff --git a/CLAUDE.md b/CLAUDE.md
index ac534a3109..c317064255 120000
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1 +1 @@
-AGENT.md
\ No newline at end of file
+AGENTS.md
diff --git a/bootstrapper-maven-plugin/pom.xml b/bootstrapper-maven-plugin/pom.xml
index c56091f0af..957681e199 100644
--- a/bootstrapper-maven-plugin/pom.xml
+++ b/bootstrapper-maven-plugin/pom.xml
@@ -22,7 +22,7 @@
io.javaoperatorsdk
java-operator-sdk
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
bootstrapper
diff --git a/caffeine-bounded-cache-support/pom.xml b/caffeine-bounded-cache-support/pom.xml
index be70ab9a2e..f80c44f235 100644
--- a/caffeine-bounded-cache-support/pom.xml
+++ b/caffeine-bounded-cache-support/pom.xml
@@ -21,7 +21,7 @@
io.javaoperatorsdk
java-operator-sdk
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
caffeine-bounded-cache-support
diff --git a/docs/assets/scss/_variables_project.scss b/docs/assets/scss/_variables_project.scss
index 3fba3772a2..e2c9097f6a 100644
--- a/docs/assets/scss/_variables_project.scss
+++ b/docs/assets/scss/_variables_project.scss
@@ -30,7 +30,7 @@ $link-hover-color: darken($link-color, 15%);
// Override link colors only in document content
.td-content {
- a {
+ a:not(.btn) {
color: $link-color !important;
&:hover {
diff --git a/docs/content/en/blog/news/read-after-write-consistency.md b/docs/content/en/blog/news/read-after-write-consistency.md
index b36b3c90a4..30c072e1c2 100644
--- a/docs/content/en/blog/news/read-after-write-consistency.md
+++ b/docs/content/en/blog/news/read-after-write-consistency.md
@@ -280,3 +280,10 @@ Covering all the edge cases was quite an effort.
Just as a highlight, I'll mention the [last one](https://github.com/operator-framework/java-operator-sdk/issues/3208).
Thank you!
+
+## Related
+
+- Same initiative in golang [controller-runtime](https://github.com/kubernetes-sigs/controller-runtime/issues/3320)
+- [Comparable Resource Versions](https://github.com/kubernetes/enhancements/tree/master/keps/sig-api-machinery/5504-comparable-resource-version) in Kubernetes
+- [Stale Controller Handling](https://github.com/kubernetes/enhancements/tree/master/keps/sig-api-machinery/5647-stale-controller-handling) KEP
+
diff --git a/docs/content/en/blog/releases/v5-3-release.md b/docs/content/en/blog/releases/v5-3-release.md
index 512df03a84..12b9bfd30e 100644
--- a/docs/content/en/blog/releases/v5-3-release.md
+++ b/docs/content/en/blog/releases/v5-3-release.md
@@ -220,6 +220,11 @@ checks.
`reconciliationFinished(..)` is extended with `RetryInfo`. `monitorSizeOf(..)` is removed.
+### `ResourceAction` relocated
+
+`ResourceAction` in `io.javaoperatorsdk.operator.processing.event.source.controller` has been
+removed. Use `io.javaoperatorsdk.operator.processing.event.source.ResourceAction` instead.
+
See the full [migration guide](/docs/migration/v5-3-migration) for details.
## Getting Started
diff --git a/docs/content/en/docs/migration/v5-3-migration.md b/docs/content/en/docs/migration/v5-3-migration.md
index e3d7c34a9d..08d6bca723 100644
--- a/docs/content/en/docs/migration/v5-3-migration.md
+++ b/docs/content/en/docs/migration/v5-3-migration.md
@@ -3,6 +3,30 @@ title: Migrating from v5.2 to v5.3
description: Migrating from v5.2 to v5.3
---
+## Automated Migration with OpenRewrite
+
+You can automatically apply all the migration changes described below using [OpenRewrite](https://docs.openrewrite.org/).
+Add the following to your `pom.xml` and run `mvn rewrite:run`:
+
+```xml
+
+ org.openrewrite.maven
+ rewrite-maven-plugin
+ 6.33.0
+
+
+ io.javaoperatorsdk.operator.migration.V5_3Migration
+
+
+
+
+ io.javaoperatorsdk
+ migration
+ 5.3.1
+
+
+
+```
## Rename of JUnit module
@@ -48,4 +72,10 @@ The following table shows the relevant method renames:
Other changes:
- `reconciliationFinished(..)` method is extended with `RetryInfo`
-- `monitorSizeOf(..)` method is removed.
\ No newline at end of file
+- `monitorSizeOf(..)` method is removed.
+
+## ResourceAction relocation
+
+The `ResourceAction` enum has been removed from
+`io.javaoperatorsdk.operator.processing.event.source.controller` use the one in package
+`io.javaoperatorsdk.operator.processing.event.source.ResourceAction`; thus update your imports accordingly.
\ No newline at end of file
diff --git a/micrometer-support/pom.xml b/micrometer-support/pom.xml
index ae3c4d0be1..d2ad4e0bc0 100644
--- a/micrometer-support/pom.xml
+++ b/micrometer-support/pom.xml
@@ -21,7 +21,7 @@
io.javaoperatorsdk
java-operator-sdk
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
micrometer-support
diff --git a/migration/pom.xml b/migration/pom.xml
new file mode 100644
index 0000000000..1976274023
--- /dev/null
+++ b/migration/pom.xml
@@ -0,0 +1,75 @@
+
+
+
+ 4.0.0
+
+ io.javaoperatorsdk
+ java-operator-sdk
+ 5.3.1-SNAPSHOT
+
+
+ migration
+ Operator SDK - Migration Recipes
+ OpenRewrite migration recipes for Java Operator SDK
+
+
+ 8.46.1
+
+
+
+
+ org.openrewrite
+ rewrite-java
+ ${openrewrite.version}
+
+
+ org.openrewrite
+ rewrite-maven
+ ${openrewrite.version}
+
+
+ org.openrewrite
+ rewrite-test
+ ${openrewrite.version}
+ test
+
+
+ org.openrewrite
+ rewrite-java-17
+ ${openrewrite.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+
+
diff --git a/migration/src/main/java/io/javaoperatorsdk/operator/migration/RemoveMethodDeclaration.java b/migration/src/main/java/io/javaoperatorsdk/operator/migration/RemoveMethodDeclaration.java
new file mode 100644
index 0000000000..33841d5acc
--- /dev/null
+++ b/migration/src/main/java/io/javaoperatorsdk/operator/migration/RemoveMethodDeclaration.java
@@ -0,0 +1,150 @@
+/*
+ * 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.migration;
+
+import java.util.Objects;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.NlsRewrite;
+import org.openrewrite.Option;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+
+public class RemoveMethodDeclaration extends Recipe {
+
+ @Option(
+ displayName = "Interface name",
+ description = "Fully qualified or simple name of the interface.",
+ example = "com.example.YourInterface")
+ String interfaceName;
+
+ @Option(
+ displayName = "Method name",
+ description = "Name of the method to remove.",
+ example = "removedMethod")
+ String methodName;
+
+ @Override
+ public String getDisplayName() {
+ return "Remove obsolete method from implementing classes";
+ }
+
+ @Override
+ public @NlsRewrite.Description String getDescription() {
+ return "Remove obsolete method from implementing classes";
+ }
+
+ @Override
+ public TreeVisitor, ExecutionContext> getVisitor() {
+ return new JavaIsoVisitor() {
+
+ @Override
+ public J.ClassDeclaration visitClassDeclaration(
+ J.ClassDeclaration classDecl, ExecutionContext ctx) {
+ J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
+
+ if (cd.getType() == null || !typeMatchesOrImplements(cd.getType())) {
+ return cd;
+ }
+
+ // Mutate the type info in place to remove the method from the declared methods list,
+ // so all AST nodes sharing this type reference stay consistent.
+ var type = cd.getType();
+ if (type instanceof JavaType.Class classType) {
+ var updatedMethods =
+ classType.getMethods().stream().filter(m -> !m.getName().equals(methodName)).toList();
+ classType.unsafeSet(
+ classType.getTypeParameters(),
+ classType.getSupertype(),
+ classType.getOwningClass(),
+ classType.getAnnotations(),
+ classType.getInterfaces(),
+ classType.getMembers(),
+ updatedMethods);
+ }
+
+ return cd;
+ }
+
+ @Override
+ public J.MethodDeclaration visitMethodDeclaration(
+ J.MethodDeclaration method, ExecutionContext ctx) {
+ if (!method.getSimpleName().equals(methodName)) {
+ return super.visitMethodDeclaration(method, ctx);
+ }
+
+ J.ClassDeclaration classDecl = getCursor().firstEnclosing(J.ClassDeclaration.class);
+ if (classDecl == null || classDecl.getType() == null) {
+ return super.visitMethodDeclaration(method, ctx);
+ }
+
+ if (typeMatchesOrImplements(classDecl.getType())) {
+ //noinspection DataFlowIssue
+ return null;
+ }
+
+ return super.visitMethodDeclaration(method, ctx);
+ }
+
+ private boolean typeMatchesOrImplements(JavaType.FullyQualified type) {
+ for (var iface : type.getInterfaces()) {
+ if (iface.getFullyQualifiedName().equals(interfaceName)
+ || typeMatchesOrImplements(iface)) {
+ return true;
+ }
+ }
+ var supertype = type.getSupertype();
+ if (supertype != null && !supertype.getFullyQualifiedName().equals("java.lang.Object")) {
+ return typeMatchesOrImplements(supertype);
+ }
+ return false;
+ }
+ };
+ }
+
+ public String getInterfaceName() {
+ return interfaceName;
+ }
+
+ public void setInterfaceName(String interfaceName) {
+ this.interfaceName = interfaceName;
+ }
+
+ public String getMethodName() {
+ return methodName;
+ }
+
+ public void setMethodName(String methodName) {
+ this.methodName = methodName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+ RemoveMethodDeclaration that = (RemoveMethodDeclaration) o;
+ return Objects.equals(interfaceName, that.interfaceName)
+ && Objects.equals(methodName, that.methodName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), interfaceName, methodName);
+ }
+}
diff --git a/migration/src/main/resources/META-INF/rewrite/v5-3-migration.yml b/migration/src/main/resources/META-INF/rewrite/v5-3-migration.yml
new file mode 100644
index 0000000000..cab26f1916
--- /dev/null
+++ b/migration/src/main/resources/META-INF/rewrite/v5-3-migration.yml
@@ -0,0 +1,124 @@
+#
+# 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.
+#
+
+---
+type: specs.openrewrite.org/v1beta/recipe
+name: io.javaoperatorsdk.operator.migration.V5_3Migration
+displayName: Migrate to Java Operator SDK v5.3
+description: >-
+ Migrates Java Operator SDK from v5.2 to v5.3, including the JUnit module
+ rename and Metrics interface method renames.
+recipeList:
+ - io.javaoperatorsdk.operator.migration.UpgradeJOSDKVersion
+ - io.javaoperatorsdk.operator.migration.RenameJUnitModule
+ - io.javaoperatorsdk.operator.migration.MetricsMethodRenames
+ - io.javaoperatorsdk.operator.migration.RemoveMonitorSizeOf
+ - io.javaoperatorsdk.operator.migration.RelocateResourceAction
+---
+type: specs.openrewrite.org/v1beta/recipe
+name: io.javaoperatorsdk.operator.migration.UpgradeJOSDKVersion
+displayName: Upgrade Java Operator SDK version to 5.3.0
+description: >-
+ Upgrades all io.javaoperatorsdk dependencies from 5.2.x to 5.3.0.
+recipeList:
+ - org.openrewrite.maven.UpgradeDependencyVersion:
+ groupId: io.javaoperatorsdk
+ artifactId: "*"
+ newVersion: 5.3.0
+ versionPattern: "5.2.*"
+---
+type: specs.openrewrite.org/v1beta/recipe
+name: io.javaoperatorsdk.operator.migration.RenameJUnitModule
+displayName: Rename JUnit module artifact
+description: >-
+ Renames the operator-framework-junit-5 artifact to operator-framework-junit.
+recipeList:
+ - org.openrewrite.maven.ChangeDependencyGroupIdAndArtifactId:
+ oldGroupId: io.javaoperatorsdk
+ oldArtifactId: operator-framework-junit-5
+ newArtifactId: operator-framework-junit
+ newVersion: 5.3.0
+---
+type: specs.openrewrite.org/v1beta/recipe
+name: io.javaoperatorsdk.operator.migration.MetricsMethodRenames
+displayName: Rename Metrics interface methods
+description: >-
+ Renames methods on the Metrics interface to match the new v5.3 API.
+recipeList:
+ - org.openrewrite.java.ChangeMethodName:
+ methodPattern: "io.javaoperatorsdk.operator.api.monitoring.Metrics reconcileCustomResource(..)"
+ newMethodName: reconciliationSubmitted
+ matchOverrides: true
+ - org.openrewrite.java.ChangeMethodName:
+ methodPattern: "io.javaoperatorsdk.operator.api.monitoring.Metrics reconciliationExecutionStarted(..)"
+ newMethodName: reconciliationStarted
+ matchOverrides: true
+ - org.openrewrite.java.ChangeMethodName:
+ methodPattern: "io.javaoperatorsdk.operator.api.monitoring.Metrics reconciliationExecutionFinished(..)"
+ newMethodName: reconciliationSucceeded
+ matchOverrides: true
+ - org.openrewrite.java.AddNullMethodArgument:
+ methodPattern: "io.javaoperatorsdk.operator.api.monitoring.Metrics failedReconciliation(..)"
+ argumentIndex: 1
+ parameterType: io.javaoperatorsdk.operator.api.reconciler.RetryInfo
+ - org.openrewrite.java.ChangeMethodName:
+ methodPattern: "io.javaoperatorsdk.operator.api.monitoring.Metrics failedReconciliation(..)"
+ newMethodName: reconciliationFailed
+ matchOverrides: true
+ - org.openrewrite.java.AddMethodParameter:
+ methodPattern: "io.javaoperatorsdk.operator.api.monitoring.Metrics reconciliationFailed(..)"
+ parameterType: io.javaoperatorsdk.operator.api.reconciler.RetryInfo
+ parameterName: retryInfo
+ parameterIndex: 1
+ - org.openrewrite.java.AddNullMethodArgument:
+ methodPattern: "io.javaoperatorsdk.operator.api.monitoring.Metrics finishedReconciliation(..)"
+ argumentIndex: 1
+ parameterType: io.javaoperatorsdk.operator.api.reconciler.RetryInfo
+ - org.openrewrite.java.ChangeMethodName:
+ methodPattern: "io.javaoperatorsdk.operator.api.monitoring.Metrics finishedReconciliation(..)"
+ newMethodName: reconciliationFinished
+ matchOverrides: true
+ - org.openrewrite.java.AddMethodParameter:
+ methodPattern: "io.javaoperatorsdk.operator.api.monitoring.Metrics reconciliationFinished(..)"
+ parameterType: io.javaoperatorsdk.operator.api.reconciler.RetryInfo
+ parameterName: retryInfo
+ parameterIndex: 1
+ - org.openrewrite.java.ChangeMethodName:
+ methodPattern: "io.javaoperatorsdk.operator.api.monitoring.Metrics cleanupDoneFor(..)"
+ newMethodName: cleanupDone
+ matchOverrides: true
+ - org.openrewrite.java.ChangeMethodName:
+ methodPattern: "io.javaoperatorsdk.operator.api.monitoring.Metrics receivedEvent(..)"
+ newMethodName: eventReceived
+ matchOverrides: true
+---
+type: specs.openrewrite.org/v1beta/recipe
+name: io.javaoperatorsdk.operator.migration.RemoveMonitorSizeOf
+displayName: Remove MonitorSizeOf
+recipeList:
+ - io.javaoperatorsdk.operator.migration.RemoveMethodDeclaration:
+ interfaceName: io.javaoperatorsdk.operator.api.monitoring.Metrics
+ methodName: monitorSizeOf
+---
+type: specs.openrewrite.org/v1beta/recipe
+name: io.javaoperatorsdk.operator.migration.RelocateResourceAction
+displayName: Relocate ResourceAction class
+description: >-
+ Moves ResourceAction import from the controller sub-package to the event source package.
+recipeList:
+ - org.openrewrite.java.ChangeType:
+ oldFullyQualifiedTypeName: io.javaoperatorsdk.operator.processing.event.source.controller.ResourceAction
+ newFullyQualifiedTypeName: io.javaoperatorsdk.operator.processing.event.source.ResourceAction
diff --git a/migration/src/test/java/io/javaoperatorsdk/operator/migration/V53MigrationTest.java b/migration/src/test/java/io/javaoperatorsdk/operator/migration/V53MigrationTest.java
new file mode 100644
index 0000000000..62ab4a412b
--- /dev/null
+++ b/migration/src/test/java/io/javaoperatorsdk/operator/migration/V53MigrationTest.java
@@ -0,0 +1,393 @@
+/*
+ * 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.migration;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+import static org.openrewrite.maven.Assertions.pomXml;
+
+class V53MigrationTest implements RewriteTest {
+
+ @Override
+ public void defaults(RecipeSpec spec) {
+ spec.recipeFromResources("io.javaoperatorsdk.operator.migration.V5_3Migration");
+ }
+
+ @Test
+ void renamesJUnitModuleInMaven() {
+ rewriteRun(
+ pomXml(
+ """
+
+ 4.0.0
+ com.example
+ test
+ 1.0
+
+
+ io.javaoperatorsdk
+ operator-framework-junit-5
+ 5.2.0
+ test
+
+
+
+ """,
+ """
+
+ 4.0.0
+ com.example
+ test
+ 1.0
+
+
+ io.javaoperatorsdk
+ operator-framework-junit
+ 5.3.0
+ test
+
+
+
+ """));
+ }
+
+ @Test
+ void renamesMetricsMethods() {
+ rewriteRun(
+ // language=java
+ java(
+ """
+ package io.javaoperatorsdk.operator.api.monitoring;
+
+ import java.util.Map;
+
+ public interface Metrics {
+ default void receivedEvent(Object event, Map metadata) {}
+ default void reconcileCustomResource(Object resource, Object retryInfo, Map metadata) {}
+ default void reconciliationExecutionStarted(Object resource, Map metadata) {}
+ default void reconciliationExecutionFinished(Object resource, Map metadata) {}
+ default void failedReconciliation(Object resource, Exception exception, Map metadata) {}
+ default void finishedReconciliation(Object resource, Map metadata) {}
+ default void cleanupDoneFor(Object resourceID, Map metadata) {}
+ }
+ """,
+ """
+ package io.javaoperatorsdk.operator.api.monitoring;
+
+ import io.javaoperatorsdk.operator.api.reconciler.RetryInfo;
+
+ import java.util.Map;
+
+ public interface Metrics {
+ default void eventReceived(Object event, Map metadata) {}
+ default void reconciliationSubmitted(Object resource, Object retryInfo, Map metadata) {}
+ default void reconciliationStarted(Object resource, Map metadata) {}
+ default void reconciliationSucceeded(Object resource, Map metadata) {}
+
+ default void reconciliationFailed(Object resource, RetryInfo retryInfo, Exception exception, Map metadata) {}
+
+ default void reconciliationFinished(Object resource, RetryInfo retryInfo, Map metadata) {}
+ default void cleanupDone(Object resourceID, Map metadata) {}
+ }
+ """));
+ }
+
+ @Test
+ void renamesMetricsMethodCallsInImplementation() {
+ rewriteRun(
+ // Stub for the Metrics interface
+ // language=java
+ java(
+ """
+ package io.javaoperatorsdk.operator.api.monitoring;
+
+ import java.util.Map;
+
+ public interface Metrics {
+ default void receivedEvent(Object event, Map metadata) {}
+ default void reconcileCustomResource(Object resource, Object retryInfo, Map metadata) {}
+ }
+ """,
+ """
+ package io.javaoperatorsdk.operator.api.monitoring;
+
+ import java.util.Map;
+
+ public interface Metrics {
+ default void eventReceived(Object event, Map metadata) {}
+ default void reconciliationSubmitted(Object resource, Object retryInfo, Map metadata) {}
+ }
+ """),
+ // Implementation that overrides the old method names
+ // language=java
+ java(
+ """
+ package com.example;
+
+ import java.util.Map;
+ import io.javaoperatorsdk.operator.api.monitoring.Metrics;
+
+ public class MyMetrics implements Metrics {
+ @Override
+ public void receivedEvent(Object event, Map metadata) {
+ System.out.println("event received");
+ }
+
+ @Override
+ public void reconcileCustomResource(Object resource, Object retryInfo, Map metadata) {
+ System.out.println("reconcile");
+ }
+ }
+ """,
+ """
+ package com.example;
+
+ import java.util.Map;
+ import io.javaoperatorsdk.operator.api.monitoring.Metrics;
+
+ public class MyMetrics implements Metrics {
+ @Override
+ public void eventReceived(Object event, Map metadata) {
+ System.out.println("event received");
+ }
+
+ @Override
+ public void reconciliationSubmitted(Object resource, Object retryInfo, Map metadata) {
+ System.out.println("reconcile");
+ }
+ }
+ """));
+ }
+
+ @Test
+ void removesMonitorSizeOfFromImplementationWithGenerics() {
+ rewriteRun(
+ // Stub for the Metrics interface (unchanged, from JOSDK library)
+ // language=java
+ java(
+ """
+ package io.javaoperatorsdk.operator.api.monitoring;
+
+ import java.util.Map;
+
+ public interface Metrics {
+ default void eventReceived(Object event, Map metadata) {}
+ default > T monitorSizeOf(T map, String name) { return map; }
+ }
+ """),
+ // Implementation that overrides monitorSizeOf with generic signature
+ // language=java
+ java(
+ """
+ package com.example;
+
+ import java.util.Map;
+ import io.javaoperatorsdk.operator.api.monitoring.Metrics;
+
+ public class MyMetrics implements Metrics {
+ @Override
+ public void eventReceived(Object event, Map metadata) {
+ System.out.println("event");
+ }
+
+ @Override
+ public > T monitorSizeOf(T map, String name) {
+ System.out.println("monitoring size");
+ return map;
+ }
+ }
+ """,
+ """
+ package com.example;
+
+ import java.util.Map;
+ import io.javaoperatorsdk.operator.api.monitoring.Metrics;
+
+ public class MyMetrics implements Metrics {
+ @Override
+ public void eventReceived(Object event, Map metadata) {
+ System.out.println("event");
+ }
+ }
+ """),
+ // Implementation without @Override annotation
+ // language=java
+ java(
+ """
+ package com.example;
+
+ import java.util.Map;
+ import io.javaoperatorsdk.operator.api.monitoring.Metrics;
+
+ public class AnotherMetrics implements Metrics {
+ public > T monitorSizeOf(T map, String name) {
+ return map;
+ }
+ }
+ """,
+ """
+ package com.example;
+
+ import java.util.Map;
+ import io.javaoperatorsdk.operator.api.monitoring.Metrics;
+
+ public class AnotherMetrics implements Metrics {
+ }
+ """));
+ }
+
+ @Test
+ void addsRetryInfoParameterToReconciliationFinished() {
+ rewriteRun(
+ // language=java
+ java(
+ """
+ package io.javaoperatorsdk.operator.api.monitoring;
+
+ import java.util.Map;
+
+ public interface Metrics {
+ default void finishedReconciliation(Object resource, Map metadata) {}
+ }
+ """,
+ """
+ package io.javaoperatorsdk.operator.api.monitoring;
+
+ import io.javaoperatorsdk.operator.api.reconciler.RetryInfo;
+
+ import java.util.Map;
+
+ public interface Metrics {
+ default void reconciliationFinished(Object resource, RetryInfo retryInfo, Map metadata) {}
+ }
+ """));
+ }
+
+ @Test
+ void addsNullRetryInfoArgumentToInvocations() {
+ rewriteRun(
+ // Stub for the Metrics interface with old method names
+ // language=java
+ java(
+ """
+ package io.javaoperatorsdk.operator.api.monitoring;
+
+ import java.util.Map;
+
+ public interface Metrics {
+ default void failedReconciliation(Object resource, Exception exception, Map metadata) {}
+ default void finishedReconciliation(Object resource, Map metadata) {}
+ }
+ """,
+ """
+ package io.javaoperatorsdk.operator.api.monitoring;
+
+ import io.javaoperatorsdk.operator.api.reconciler.RetryInfo;
+
+ import java.util.Map;
+
+ public interface Metrics {
+ default void reconciliationFailed(Object resource, RetryInfo retryInfo, Exception exception, Map metadata) {}
+
+ default void reconciliationFinished(Object resource, RetryInfo retryInfo, Map metadata) {}
+ }
+ """),
+ // Stub for RetryInfo
+ // language=java
+ java(
+ """
+ package io.javaoperatorsdk.operator.api.reconciler;
+ public interface RetryInfo {}
+ """),
+ // Class that calls the old methods
+ // language=java
+ java(
+ """
+ package com.example;
+
+ import java.util.Map;
+ import io.javaoperatorsdk.operator.api.monitoring.Metrics;
+
+ public class MetricsCaller {
+ public void report(Metrics metrics, Object resource, Exception ex, Map meta) {
+ metrics.failedReconciliation(resource, ex, meta);
+ metrics.finishedReconciliation(resource, meta);
+ }
+ }
+ """,
+ """
+ package com.example;
+
+ import java.util.Map;
+ import io.javaoperatorsdk.operator.api.monitoring.Metrics;
+
+ public class MetricsCaller {
+ public void report(Metrics metrics, Object resource, Exception ex, Map meta) {
+ metrics.reconciliationFailed(resource, null, ex, meta);
+ metrics.reconciliationFinished(resource, null, meta);
+ }
+ }
+ """));
+ }
+
+ @Test
+ void relocatesResourceActionImport() {
+ rewriteRun(
+ // Stub for the old ResourceAction location
+ // language=java
+ java(
+ """
+ package io.javaoperatorsdk.operator.processing.event.source.controller;
+
+ public class ResourceAction {
+ }
+ """,
+ """
+ package io.javaoperatorsdk.operator.processing.event.source;
+
+ public class ResourceAction {
+ }
+ """),
+ // Class that imports ResourceAction from the old package
+ // language=java
+ java(
+ """
+ package com.example;
+
+ import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceAction;
+
+ public class MyHandler {
+ public void handle(ResourceAction action) {
+ System.out.println(action);
+ }
+ }
+ """,
+ """
+ package com.example;
+
+ import io.javaoperatorsdk.operator.processing.event.source.ResourceAction;
+
+ public class MyHandler {
+ public void handle(ResourceAction action) {
+ System.out.println(action);
+ }
+ }
+ """));
+ }
+}
diff --git a/operator-framework-bom/pom.xml b/operator-framework-bom/pom.xml
index 643d000c2f..5ca3705bc1 100644
--- a/operator-framework-bom/pom.xml
+++ b/operator-framework-bom/pom.xml
@@ -21,7 +21,7 @@
io.javaoperatorsdk
operator-framework-bom
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
pom
Operator SDK - Bill of Materials
Java SDK for implementing Kubernetes operators
diff --git a/operator-framework-core/pom.xml b/operator-framework-core/pom.xml
index 2356433ca9..61ff7fd0f7 100644
--- a/operator-framework-core/pom.xml
+++ b/operator-framework-core/pom.xml
@@ -21,7 +21,7 @@
io.javaoperatorsdk
java-operator-sdk
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
../pom.xml
diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java
index 978deda333..26543e8322 100644
--- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java
+++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java
@@ -191,8 +191,8 @@ public void handleRecentResourceCreate(ResourceID resourceID, R resource) {
@Override
public Optional get(ResourceID resourceID) {
+ // The order of reading from these caches matters
Optional resource = temporaryResourceCache.getResourceFromCache(resourceID);
- var res = cache.get(resourceID);
if (comparableResourceVersions
&& resource.isPresent()
&& ReconcilerUtilsInternal.compareResourceVersions(
@@ -201,13 +201,16 @@ public Optional get(ResourceID resourceID) {
> 0) {
log.debug("Latest resource found in temporary cache for Resource ID: {}", resourceID);
return resource;
+ } else {
+ // this needs to happen after comparison to ensure correctness
+ var resFromInformer = cache.get(resourceID);
+ log.debug(
+ "Resource {} in temp cache; {}found in informer cache" + ", for Resource ID: {}",
+ resource.isPresent() ? "obsolete" : "not found",
+ resFromInformer.isPresent() ? "" : "not ",
+ resourceID);
+ return resFromInformer;
}
- log.debug(
- "Resource not found, or older, in temporary cache. Found in informer cache {}, for"
- + " Resource ID: {}",
- res.isPresent(),
- resourceID);
- return res;
}
/**
diff --git a/operator-framework-junit/pom.xml b/operator-framework-junit/pom.xml
index aa18d5c778..5813f9408c 100644
--- a/operator-framework-junit/pom.xml
+++ b/operator-framework-junit/pom.xml
@@ -21,7 +21,7 @@
io.javaoperatorsdk
java-operator-sdk
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
operator-framework-junit
diff --git a/operator-framework/pom.xml b/operator-framework/pom.xml
index f94dfa757d..1588590401 100644
--- a/operator-framework/pom.xml
+++ b/operator-framework/pom.xml
@@ -21,7 +21,7 @@
io.javaoperatorsdk
java-operator-sdk
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
operator-framework
diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cachingfilteringupdate/CachingFilteringUpdateReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cachingfilteringupdate/CachingFilteringUpdateReconciler.java
index 1bd60eb2c9..0e9a953129 100644
--- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cachingfilteringupdate/CachingFilteringUpdateReconciler.java
+++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cachingfilteringupdate/CachingFilteringUpdateReconciler.java
@@ -43,19 +43,31 @@ public UpdateControl reconcile(
CachingFilteringUpdateCustomResource resource,
Context context) {
- context.resourceOperations().serverSideApply(prepareCM(resource));
+ context.resourceOperations().serverSideApply(prepareCM(resource, 1));
var cachedCM = context.getSecondaryResource(ConfigMap.class);
if (cachedCM.isEmpty()) {
issueFound.set(true);
throw new IllegalStateException("Error for resource: " + ResourceID.fromResource(resource));
}
+ var updated = context.resourceOperations().serverSideApply(prepareCM(resource, 2));
+ cachedCM = context.getSecondaryResource(ConfigMap.class);
+ if (!cachedCM
+ .orElseThrow()
+ .getMetadata()
+ .getResourceVersion()
+ .equals(updated.getMetadata().getResourceVersion())) {
+ issueFound.set(true);
+ throw new IllegalStateException(
+ "Update error for resource: " + ResourceID.fromResource(resource));
+ }
+
ensureStatusExists(resource);
resource.getStatus().setUpdated(true);
return UpdateControl.patchStatus(resource);
}
- private static ConfigMap prepareCM(CachingFilteringUpdateCustomResource p) {
+ private static ConfigMap prepareCM(CachingFilteringUpdateCustomResource p, int num) {
var cm =
new ConfigMapBuilder()
.withMetadata(
@@ -63,7 +75,7 @@ private static ConfigMap prepareCM(CachingFilteringUpdateCustomResource p) {
.withName(p.getMetadata().getName())
.withNamespace(p.getMetadata().getNamespace())
.build())
- .withData(Map.of("name", p.getMetadata().getName()))
+ .withData(Map.of("name", p.getMetadata().getName(), "num", "" + num))
.build();
cm.addOwnerReference(p);
return cm;
diff --git a/pom.xml b/pom.xml
index 2c420d93ef..d1562cb1a0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
io.javaoperatorsdk
java-operator-sdk
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
pom
Operator SDK for Java
Java SDK for implementing Kubernetes operators
@@ -54,6 +54,7 @@
caffeine-bounded-cache-support
bootstrapper-maven-plugin
test-index-processor
+ migration
diff --git a/sample-operators/controller-namespace-deletion/pom.xml b/sample-operators/controller-namespace-deletion/pom.xml
index 4cdfe1f2bd..c5bfaa47bc 100644
--- a/sample-operators/controller-namespace-deletion/pom.xml
+++ b/sample-operators/controller-namespace-deletion/pom.xml
@@ -22,7 +22,7 @@
io.javaoperatorsdk
sample-operators
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
sample-controller-namespace-deletion
diff --git a/sample-operators/leader-election/pom.xml b/sample-operators/leader-election/pom.xml
index 4f896485d1..ce6c1bad41 100644
--- a/sample-operators/leader-election/pom.xml
+++ b/sample-operators/leader-election/pom.xml
@@ -22,7 +22,7 @@
io.javaoperatorsdk
sample-operators
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
sample-leader-election
diff --git a/sample-operators/metrics-processing/pom.xml b/sample-operators/metrics-processing/pom.xml
index 625a58a9de..33d18acbeb 100644
--- a/sample-operators/metrics-processing/pom.xml
+++ b/sample-operators/metrics-processing/pom.xml
@@ -22,7 +22,7 @@
io.javaoperatorsdk
sample-operators
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
sample-metrics-processing
@@ -66,7 +66,7 @@
org.yaml
snakeyaml
- 2.3
+ 2.6
org.apache.logging.log4j
diff --git a/sample-operators/mysql-schema/pom.xml b/sample-operators/mysql-schema/pom.xml
index 2055a9933d..c4612cf82a 100644
--- a/sample-operators/mysql-schema/pom.xml
+++ b/sample-operators/mysql-schema/pom.xml
@@ -22,7 +22,7 @@
io.javaoperatorsdk
sample-operators
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
sample-mysql-schema-operator
diff --git a/sample-operators/pom.xml b/sample-operators/pom.xml
index c8a70b2701..7abd6048a9 100644
--- a/sample-operators/pom.xml
+++ b/sample-operators/pom.xml
@@ -22,7 +22,7 @@
io.javaoperatorsdk
java-operator-sdk
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
sample-operators
diff --git a/sample-operators/tomcat-operator/pom.xml b/sample-operators/tomcat-operator/pom.xml
index b7c3b05c98..6d20175521 100644
--- a/sample-operators/tomcat-operator/pom.xml
+++ b/sample-operators/tomcat-operator/pom.xml
@@ -22,7 +22,7 @@
io.javaoperatorsdk
sample-operators
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
sample-tomcat-operator
diff --git a/sample-operators/webpage/pom.xml b/sample-operators/webpage/pom.xml
index c50366e37e..f700c27987 100644
--- a/sample-operators/webpage/pom.xml
+++ b/sample-operators/webpage/pom.xml
@@ -22,7 +22,7 @@
io.javaoperatorsdk
sample-operators
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
sample-webpage-operator
diff --git a/test-index-processor/pom.xml b/test-index-processor/pom.xml
index 2ae7c5f454..d42c7b912a 100644
--- a/test-index-processor/pom.xml
+++ b/test-index-processor/pom.xml
@@ -22,7 +22,7 @@
io.javaoperatorsdk
java-operator-sdk
- 999-SNAPSHOT
+ 5.3.1-SNAPSHOT
test-index-processor