Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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.3-SNAPSHOT</version>
<version>5.3.0-SNAPSHOT</version>
</parent>

<artifactId>bootstrapper</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,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.3-SNAPSHOT</version>
<version>5.3.0-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
29 changes: 29 additions & 0 deletions docs/content/en/docs/migration/v5-3-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
title: Migrating from v5.2 to v5.3
description: Migrating from v5.2 to v5.3
---


## Renamed JUnit Module

If you use JUnit extension in your test just rename it from:

```
<dependency>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>operator-framework-junit-5</artifactId>
<version>5.2.x<version>
<scope>test</scope>
</dependency>
```

to

```
<dependency>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>operator-framework-junit</artifactId>
<version>5.3.0<version>
<scope>test</scope>
</dependency>
```
4 changes: 2 additions & 2 deletions micrometer-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.3-SNAPSHOT</version>
<version>5.3.0-SNAPSHOT</version>
</parent>

<artifactId>micrometer-support</artifactId>
Expand Down Expand Up @@ -58,7 +58,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
6 changes: 3 additions & 3 deletions operator-framework-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

<groupId>io.javaoperatorsdk</groupId>
<artifactId>operator-framework-bom</artifactId>
<version>5.2.3-SNAPSHOT</version>
<version>5.3.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Operator SDK - Bill of Materials</name>
<description>Java SDK for implementing Kubernetes operators</description>
Expand Down Expand Up @@ -55,7 +55,7 @@
<maven-source-plugin.version>3.4.0</maven-source-plugin.version>
<maven-javadoc-plugin.version>3.12.0</maven-javadoc-plugin.version>
<spotless.version>3.1.0</spotless.version>
<central-publishing-maven-plugin.version>0.10.0</central-publishing-maven-plugin.version>
<central-publishing-maven-plugin.version>0.9.0</central-publishing-maven-plugin.version>
</properties>

<dependencyManagement>
Expand All @@ -77,7 +77,7 @@
</dependency>
<dependency>
<groupId>io.javaoperatorsdk</groupId>
<artifactId>operator-framework-junit-5</artifactId>
<artifactId>operator-framework-junit</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
Expand Down
2 changes: 1 addition & 1 deletion operator-framework-core/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.3-SNAPSHOT</version>
<version>5.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ public <P extends HasMetadata> RegisteredController<P> register(
"Cannot register reconciler with name "
+ reconciler.getClass().getCanonicalName()
+ " reconciler named "
+ ReconcilerUtils.getNameFor(reconciler)
+ ReconcilerUtilsInternal.getNameFor(reconciler)
+ " because its configuration cannot be found.\n"
+ " Known reconcilers are: "
+ configurationService.getKnownReconcilerNames());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@
import io.fabric8.kubernetes.client.utils.Serialization;
import io.javaoperatorsdk.operator.api.reconciler.Constants;
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
import io.javaoperatorsdk.operator.api.reconciler.NonComparableResourceVersionException;
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;

@SuppressWarnings("rawtypes")
public class ReconcilerUtils {
public class ReconcilerUtilsInternal {

private static final String FINALIZER_NAME_SUFFIX = "/finalizer";
protected static final String MISSING_GROUP_SUFFIX = ".javaoperatorsdk.io";
Expand All @@ -46,7 +47,7 @@ public class ReconcilerUtils {
Pattern.compile(".*http(s?)://[^/]*/api(s?)/(\\S*).*"); // NOSONAR: input is controlled

// prevent instantiation of util class
private ReconcilerUtils() {}
private ReconcilerUtilsInternal() {}

public static boolean isFinalizerValid(String finalizer) {
return HasMetadata.validateFinalizer(finalizer);
Expand Down Expand Up @@ -241,4 +242,123 @@ private static boolean matchesResourceType(
}
return false;
}

/**
* Compares resource versions of two resources. This is a convenience method that extracts the
* resource versions from the metadata and delegates to {@link
* #validateAndCompareResourceVersions(String, String)}.
*
* @param h1 first resource
* @param h2 second resource
* @return negative if h1 is older, zero if equal, positive if h1 is newer
* @throws NonComparableResourceVersionException if either resource version is invalid
*/
public static int validateAndCompareResourceVersions(HasMetadata h1, HasMetadata h2) {
return validateAndCompareResourceVersions(
h1.getMetadata().getResourceVersion(), h2.getMetadata().getResourceVersion());
}

/**
* Compares the resource versions of two Kubernetes resources.
*
* <p>This method extracts the resource versions from the metadata of both resources and delegates
* to {@link #compareResourceVersions(String, String)} for the actual comparison.
*
* @param h1 the first resource to compare
* @param h2 the second resource to compare
* @return a negative integer if h1's version is less than h2's version, zero if they are equal,
* or a positive integer if h1's version is greater than h2's version
* @see #compareResourceVersions(String, String)
*/
public static int compareResourceVersions(HasMetadata h1, HasMetadata h2) {
return compareResourceVersions(
h1.getMetadata().getResourceVersion(), h2.getMetadata().getResourceVersion());
}

/**
* Compares two resource version strings using a length-first, then lexicographic comparison
* algorithm.
*
* <p>The comparison is performed in two steps:
*
* <ol>
* <li>First, compare the lengths of the version strings. A longer version string is considered
* greater than a shorter one. This works correctly for numeric versions because larger
* numbers have more digits (e.g., "100" > "99").
* <li>If the lengths are equal, perform a character-by-character lexicographic comparison until
* a difference is found.
* </ol>
*
* <p>This algorithm is more efficient than parsing the versions as numbers, especially for
* Kubernetes resource versions which are typically monotonically increasing numeric strings.
*
* <p><strong>Note:</strong> This method does not validate that the input strings are numeric. For
* validated numeric comparison, use {@link #validateAndCompareResourceVersions(String, String)}.
*
* @param v1 the first resource version string
* @param v2 the second resource version string
* @return a negative integer if v1 is less than v2, zero if they are equal, or a positive integer
* if v1 is greater than v2
* @see #validateAndCompareResourceVersions(String, String)
*/
public static int compareResourceVersions(String v1, String v2) {
int comparison = v1.length() - v2.length();
if (comparison != 0) {
return comparison;
}
for (int i = 0; i < v2.length(); i++) {
int comp = v1.charAt(i) - v2.charAt(i);
if (comp != 0) {
return comp;
}
}
return 0;
}

/**
* Compares two Kubernetes resource versions numerically. Kubernetes resource versions are
* expected to be numeric strings that increase monotonically. This method assumes both versions
* are valid numeric strings without leading zeros.
*
* @param v1 first resource version
* @param v2 second resource version
* @return negative if v1 is older, zero if equal, positive if v1 is newer
* @throws NonComparableResourceVersionException if either resource version is empty, has leading
* zeros, or contains non-numeric characters
*/
public static int validateAndCompareResourceVersions(String v1, String v2) {
int v1Length = validateResourceVersion(v1);
int v2Length = validateResourceVersion(v2);
int comparison = v1Length - v2Length;
if (comparison != 0) {
return comparison;
}
for (int i = 0; i < v2Length; i++) {
int comp = v1.charAt(i) - v2.charAt(i);
if (comp != 0) {
return comp;
}
}
return 0;
}

private static int validateResourceVersion(String v1) {
int v1Length = v1.length();
if (v1Length == 0) {
throw new NonComparableResourceVersionException("Resource version is empty");
}
for (int i = 0; i < v1Length; i++) {
char char1 = v1.charAt(i);
if (char1 == '0') {
if (i == 0) {
throw new NonComparableResourceVersionException(
"Resource version cannot begin with 0: " + v1);
}
} else if (char1 < '0' || char1 > '9') {
throw new NonComparableResourceVersionException(
"Non numeric characters in resource version: " + v1);
}
}
return v1Length;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.javaoperatorsdk.operator.ReconcilerUtils;
import io.javaoperatorsdk.operator.ReconcilerUtilsInternal;
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;

/**
Expand Down Expand Up @@ -145,7 +145,7 @@ private String getReconcilersNameMessage() {
}

protected <R extends HasMetadata> String keyFor(Reconciler<R> reconciler) {
return ReconcilerUtils.getNameFor(reconciler);
return ReconcilerUtilsInternal.getNameFor(reconciler);
}

@SuppressWarnings("unused")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

import io.fabric8.kubernetes.api.model.HasMetadata;
import io.fabric8.kubernetes.client.KubernetesClient;
import io.javaoperatorsdk.operator.ReconcilerUtils;
import io.javaoperatorsdk.operator.ReconcilerUtilsInternal;
import io.javaoperatorsdk.operator.api.config.Utils.Configurator;
import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceConfigurationResolver;
import io.javaoperatorsdk.operator.api.config.dependent.DependentResourceSpec;
Expand Down Expand Up @@ -265,7 +265,7 @@ private <P extends HasMetadata> ResolvedControllerConfiguration<P> controllerCon
io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration annotation) {
final var resourceClass = getResourceClassResolver().getPrimaryResourceClass(reconcilerClass);

final var name = ReconcilerUtils.getNameFor(reconcilerClass);
final var name = ReconcilerUtilsInternal.getNameFor(reconcilerClass);
final var generationAware =
valueOrDefaultFromAnnotation(
annotation,
Expand Down
Loading