Skip to content

feat: detect dependent resource API version changes - #3536

Open
hej090224 wants to merge 1 commit into
operator-framework:nextfrom
hej090224:feat/2644-dependent-api-version-update
Open

feat: detect dependent resource API version changes#3536
hej090224 wants to merge 1 commit into
operator-framework:nextfrom
hej090224:feat/2644-dependent-api-version-update

Conversation

@hej090224

Copy link
Copy Markdown
Contributor

Summary

  • Add an opt-in, experimental detectApiVersionChange option on @KubernetesDependent to detect when a dependent resource's API version has changed since the operator last applied it, and request a one-time update in that case.
  • Record the API version the operator applies in the javaoperatorsdk.io/last-applied-api-version annotation, following the same pattern as the existing javaoperatorsdk.io/previous annotation.
  • Reuse the existing matching machinery (both SSA-based and non-SSA) to compare the marker instead of adding special-case matcher logic, so behavior is consistent across both paths.
  • Default behavior is unchanged: the feature is disabled by default and no marker annotation is ever added or read unless explicitly enabled.

Motivation

When a dependent resource's CRD gains a new API version and the operator is upgraded to target it, comparing actualResource.getApiVersion() with the desired resource's API version is not a reliable way to detect resources that still need to be updated: the Kubernetes API server serves a resource using the requested, served API version regardless of what it is actually stored as, so this comparison would always trivially match. This is why KubernetesDependentResource already ignores apiVersion entirely in both matchers.

Instead of trying to infer the actual stored/storage version (which JOSDK cannot reliably observe, and which tools like StorageVersionMigration exist to address), this PR lets JOSDK track what the operator itself last applied, using a persistent annotation marker, discussed in #2644.

When detectApiVersionChange is enabled:

  • On every create/update/match, the target (desired) resource is marked with the API version the operator is currently using.
  • The existing matcher (SSA or non-SSA) naturally detects a mismatch when the actual resource's recorded marker differs from - or is missing relative to - the desired marker, since it's just another annotation diff.
  • Once updated, the actual resource's marker matches the desired one again, so no further updates are triggered until the API version changes again.
  • This includes resources created before the feature was enabled: a missing marker is treated as a mismatch, causing a one-time update, after which matching succeeds normally.

This is explicitly not a replacement for Kubernetes' StorageVersionMigration and does not attempt to read or infer the actual stored representation of the resource.

Public API

  • New @KubernetesDependent(detectApiVersionChange = true) annotation attribute (default false), marked @Experimental.
  • New KubernetesDependentResourceConfig#detectApiVersionChange() and a new (additive) constructor overload; existing constructors are unchanged.
  • New KubernetesDependentResourceConfigBuilder#withDetectApiVersionChange(boolean).
  • New KubernetesDependentResource.LAST_APPLIED_API_VERSION_ANNOTATION_KEY constant (javaoperatorsdk.io/last-applied-api-version).

No breaking changes.

Testing

  • ./mvnw -pl operator-framework-core -am test -Dtest='KubernetesDependentResourceApiVersionChangeTest,KubernetesDependentConverterTest'
  • ./mvnw -pl operator-framework-core -am test -Dtest='GenericKubernetesResourceMatcherTest,SSABasedGenericKubernetesResourceMatcherTest,KubernetesDependentResourceTest,DependentResourceConfigurationResolverTest'
  • ./mvnw -pl operator-framework-core -am test (full core module, 706 tests)
  • ./mvnw spotless:apply / ./mvnw spotless:check
  • ./mvnw clean install -pl '!migration' (the migration module's OpenRewrite-based tests fail in this environment due to a pre-existing JDK 25 / OpenRewrite javac-internals incompatibility, unrelated to this change and not touched by it)

Fixes #2644

Copilot AI review requested due to automatic review settings August 3, 2026 17:23
@openshift-ci
openshift-ci Bot requested review from csviri and metacosm August 3, 2026 17:23
Add an opt-in, experimental detectApiVersionChange option on
@KubernetesDependent that records the API version the operator applies
in the javaoperatorsdk.io/last-applied-api-version annotation. The
regular matcher then detects a mismatch when that marker differs from
(or is missing relative to) the currently applied API version, causing
a one-time update without triggering repeated reconciliations once the
resource is up-to-date. Disabled by default, so existing behavior and
matching are unaffected unless explicitly enabled.

Fixes operator-framework#2644

Signed-off-by: hej090224 <fc49854985@gmail.com>
@hej090224
hej090224 force-pushed the feat/2644-dependent-api-version-update branch from d322648 to f0530e1 Compare August 3, 2026 17:33

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds an opt-in, experimental mechanism for dependent resources to detect API version changes by persisting the operator-applied API version in an annotation, so the existing matcher logic can trigger a one-time update when that marker changes.

Changes:

  • Introduces detectApiVersionChange on @KubernetesDependent, plus config + builder wiring to propagate the flag.
  • Adds javaoperatorsdk.io/last-applied-api-version marker annotation support in KubernetesDependentResource.
  • Adds focused unit tests and documentation covering both SSA and non-SSA matcher behavior.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java Adds marker annotation constant and writes marker when feature enabled.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java Adds config flag, default, constructor overload, and getter.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfigBuilder.java Adds builder support for detectApiVersionChange.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentConverter.java Wires annotation attribute into config creation.
operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java Adds experimental annotation attribute and documentation.
operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceApiVersionChangeTest.java New tests validating marker-driven mismatch behavior for SSA and non-SSA.
operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentConverterTest.java New focused test for annotation→config wiring for the new flag.
docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md Documents the new experimental feature and its behavior/limitations.

Comment on lines +195 to +207
private void addLastAppliedApiVersion(R target) {
if (kubernetesDependentResourceConfig == null
|| !kubernetesDependentResourceConfig.detectApiVersionChange()) {
return;
}
var apiVersion = target.getApiVersion();
if (apiVersion != null) {
target
.getMetadata()
.getAnnotations()
.put(LAST_APPLIED_API_VERSION_ANNOTATION_KEY, apiVersion);
}
}
Comment on lines +195 to +207
private void addLastAppliedApiVersion(R target) {
if (kubernetesDependentResourceConfig == null
|| !kubernetesDependentResourceConfig.detectApiVersionChange()) {
return;
}
var apiVersion = target.getApiVersion();
if (apiVersion != null) {
target
.getMetadata()
.getAnnotations()
.put(LAST_APPLIED_API_VERSION_ANNOTATION_KEY, apiVersion);
}
}
Comment on lines +313 to +318
private static class ConfigMapDependentResourceForTest
extends KubernetesDependentResource<GenericKubernetesResource, HasMetadata> {
public ConfigMapDependentResourceForTest() {
super(GenericKubernetesResource.class, null);
}
}
Comment on lines +178 to +182
var result = dr.match(actual, desired, primary(), context);

assertThat(result.matched()).isNotNull();
assertThat(desired.getMetadata().getAnnotations())
.doesNotContainKey(KubernetesDependentResource.LAST_APPLIED_API_VERSION_ANNOTATION_KEY);
Comment on lines +75 to +77
* <p>When enabled, JOSDK records the API version it applies in the {@code
* javaoperatorsdk.io/last-applied-api-version} annotation. On subsequent reconciliations, the
* resource is considered mismatched (and thus updated) if that recorded marker differs from the
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants