Skip to content

Commit 4d384fd

Browse files
committed
improve: move compare resource version methods to internal utils (#3137)
This should not be user facing. At least not in any obvious scenerio. Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
1 parent 120a28b commit 4d384fd

File tree

7 files changed

+265
-264
lines changed

7 files changed

+265
-264
lines changed

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/ReconcilerUtilsInternal.java

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import io.fabric8.kubernetes.client.utils.Serialization;
3232
import io.javaoperatorsdk.operator.api.reconciler.Constants;
3333
import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration;
34+
import io.javaoperatorsdk.operator.api.reconciler.NonComparableResourceVersionException;
3435
import io.javaoperatorsdk.operator.api.reconciler.Reconciler;
3536

3637
@SuppressWarnings("rawtypes")
@@ -241,4 +242,123 @@ private static boolean matchesResourceType(
241242
}
242243
return false;
243244
}
245+
246+
/**
247+
* Compares resource versions of two resources. This is a convenience method that extracts the
248+
* resource versions from the metadata and delegates to {@link
249+
* #validateAndCompareResourceVersions(String, String)}.
250+
*
251+
* @param h1 first resource
252+
* @param h2 second resource
253+
* @return negative if h1 is older, zero if equal, positive if h1 is newer
254+
* @throws NonComparableResourceVersionException if either resource version is invalid
255+
*/
256+
public static int validateAndCompareResourceVersions(HasMetadata h1, HasMetadata h2) {
257+
return validateAndCompareResourceVersions(
258+
h1.getMetadata().getResourceVersion(), h2.getMetadata().getResourceVersion());
259+
}
260+
261+
/**
262+
* Compares the resource versions of two Kubernetes resources.
263+
*
264+
* <p>This method extracts the resource versions from the metadata of both resources and delegates
265+
* to {@link #compareResourceVersions(String, String)} for the actual comparison.
266+
*
267+
* @param h1 the first resource to compare
268+
* @param h2 the second resource to compare
269+
* @return a negative integer if h1's version is less than h2's version, zero if they are equal,
270+
* or a positive integer if h1's version is greater than h2's version
271+
* @see #compareResourceVersions(String, String)
272+
*/
273+
public static int compareResourceVersions(HasMetadata h1, HasMetadata h2) {
274+
return compareResourceVersions(
275+
h1.getMetadata().getResourceVersion(), h2.getMetadata().getResourceVersion());
276+
}
277+
278+
/**
279+
* Compares two resource version strings using a length-first, then lexicographic comparison
280+
* algorithm.
281+
*
282+
* <p>The comparison is performed in two steps:
283+
*
284+
* <ol>
285+
* <li>First, compare the lengths of the version strings. A longer version string is considered
286+
* greater than a shorter one. This works correctly for numeric versions because larger
287+
* numbers have more digits (e.g., "100" > "99").
288+
* <li>If the lengths are equal, perform a character-by-character lexicographic comparison until
289+
* a difference is found.
290+
* </ol>
291+
*
292+
* <p>This algorithm is more efficient than parsing the versions as numbers, especially for
293+
* Kubernetes resource versions which are typically monotonically increasing numeric strings.
294+
*
295+
* <p><strong>Note:</strong> This method does not validate that the input strings are numeric. For
296+
* validated numeric comparison, use {@link #validateAndCompareResourceVersions(String, String)}.
297+
*
298+
* @param v1 the first resource version string
299+
* @param v2 the second resource version string
300+
* @return a negative integer if v1 is less than v2, zero if they are equal, or a positive integer
301+
* if v1 is greater than v2
302+
* @see #validateAndCompareResourceVersions(String, String)
303+
*/
304+
public static int compareResourceVersions(String v1, String v2) {
305+
int comparison = v1.length() - v2.length();
306+
if (comparison != 0) {
307+
return comparison;
308+
}
309+
for (int i = 0; i < v2.length(); i++) {
310+
int comp = v1.charAt(i) - v2.charAt(i);
311+
if (comp != 0) {
312+
return comp;
313+
}
314+
}
315+
return 0;
316+
}
317+
318+
/**
319+
* Compares two Kubernetes resource versions numerically. Kubernetes resource versions are
320+
* expected to be numeric strings that increase monotonically. This method assumes both versions
321+
* are valid numeric strings without leading zeros.
322+
*
323+
* @param v1 first resource version
324+
* @param v2 second resource version
325+
* @return negative if v1 is older, zero if equal, positive if v1 is newer
326+
* @throws NonComparableResourceVersionException if either resource version is empty, has leading
327+
* zeros, or contains non-numeric characters
328+
*/
329+
public static int validateAndCompareResourceVersions(String v1, String v2) {
330+
int v1Length = validateResourceVersion(v1);
331+
int v2Length = validateResourceVersion(v2);
332+
int comparison = v1Length - v2Length;
333+
if (comparison != 0) {
334+
return comparison;
335+
}
336+
for (int i = 0; i < v2Length; i++) {
337+
int comp = v1.charAt(i) - v2.charAt(i);
338+
if (comp != 0) {
339+
return comp;
340+
}
341+
}
342+
return 0;
343+
}
344+
345+
private static int validateResourceVersion(String v1) {
346+
int v1Length = v1.length();
347+
if (v1Length == 0) {
348+
throw new NonComparableResourceVersionException("Resource version is empty");
349+
}
350+
for (int i = 0; i < v1Length; i++) {
351+
char char1 = v1.charAt(i);
352+
if (char1 == '0') {
353+
if (i == 0) {
354+
throw new NonComparableResourceVersionException(
355+
"Resource version cannot begin with 0: " + v1);
356+
}
357+
} else if (char1 < '0' || char1 > '9') {
358+
throw new NonComparableResourceVersionException(
359+
"Non numeric characters in resource version: " + v1);
360+
}
361+
}
362+
return v1Length;
363+
}
244364
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ReconcileUtils.java

Lines changed: 0 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -595,123 +595,4 @@ public static <P extends HasMetadata> P addFinalizerWithSSA(
595595
e);
596596
}
597597
}
598-
599-
/**
600-
* Compares resource versions of two resources. This is a convenience method that extracts the
601-
* resource versions from the metadata and delegates to {@link
602-
* #validateAndCompareResourceVersions(String, String)}.
603-
*
604-
* @param h1 first resource
605-
* @param h2 second resource
606-
* @return negative if h1 is older, zero if equal, positive if h1 is newer
607-
* @throws NonComparableResourceVersionException if either resource version is invalid
608-
*/
609-
public static int validateAndCompareResourceVersions(HasMetadata h1, HasMetadata h2) {
610-
return validateAndCompareResourceVersions(
611-
h1.getMetadata().getResourceVersion(), h2.getMetadata().getResourceVersion());
612-
}
613-
614-
/**
615-
* Compares the resource versions of two Kubernetes resources.
616-
*
617-
* <p>This method extracts the resource versions from the metadata of both resources and delegates
618-
* to {@link #compareResourceVersions(String, String)} for the actual comparison.
619-
*
620-
* @param h1 the first resource to compare
621-
* @param h2 the second resource to compare
622-
* @return a negative integer if h1's version is less than h2's version, zero if they are equal,
623-
* or a positive integer if h1's version is greater than h2's version
624-
* @see #compareResourceVersions(String, String)
625-
*/
626-
public static int compareResourceVersions(HasMetadata h1, HasMetadata h2) {
627-
return compareResourceVersions(
628-
h1.getMetadata().getResourceVersion(), h2.getMetadata().getResourceVersion());
629-
}
630-
631-
/**
632-
* Compares two resource version strings using a length-first, then lexicographic comparison
633-
* algorithm.
634-
*
635-
* <p>The comparison is performed in two steps:
636-
*
637-
* <ol>
638-
* <li>First, compare the lengths of the version strings. A longer version string is considered
639-
* greater than a shorter one. This works correctly for numeric versions because larger
640-
* numbers have more digits (e.g., "100" > "99").
641-
* <li>If the lengths are equal, perform a character-by-character lexicographic comparison until
642-
* a difference is found.
643-
* </ol>
644-
*
645-
* <p>This algorithm is more efficient than parsing the versions as numbers, especially for
646-
* Kubernetes resource versions which are typically monotonically increasing numeric strings.
647-
*
648-
* <p><strong>Note:</strong> This method does not validate that the input strings are numeric. For
649-
* validated numeric comparison, use {@link #validateAndCompareResourceVersions(String, String)}.
650-
*
651-
* @param v1 the first resource version string
652-
* @param v2 the second resource version string
653-
* @return a negative integer if v1 is less than v2, zero if they are equal, or a positive integer
654-
* if v1 is greater than v2
655-
* @see #validateAndCompareResourceVersions(String, String)
656-
*/
657-
public static int compareResourceVersions(String v1, String v2) {
658-
int comparison = v1.length() - v2.length();
659-
if (comparison != 0) {
660-
return comparison;
661-
}
662-
for (int i = 0; i < v2.length(); i++) {
663-
int comp = v1.charAt(i) - v2.charAt(i);
664-
if (comp != 0) {
665-
return comp;
666-
}
667-
}
668-
return 0;
669-
}
670-
671-
/**
672-
* Compares two Kubernetes resource versions numerically. Kubernetes resource versions are
673-
* expected to be numeric strings that increase monotonically. This method assumes both versions
674-
* are valid numeric strings without leading zeros.
675-
*
676-
* @param v1 first resource version
677-
* @param v2 second resource version
678-
* @return negative if v1 is older, zero if equal, positive if v1 is newer
679-
* @throws NonComparableResourceVersionException if either resource version is empty, has leading
680-
* zeros, or contains non-numeric characters
681-
*/
682-
public static int validateAndCompareResourceVersions(String v1, String v2) {
683-
int v1Length = validateResourceVersion(v1);
684-
int v2Length = validateResourceVersion(v2);
685-
int comparison = v1Length - v2Length;
686-
if (comparison != 0) {
687-
return comparison;
688-
}
689-
for (int i = 0; i < v2Length; i++) {
690-
int comp = v1.charAt(i) - v2.charAt(i);
691-
if (comp != 0) {
692-
return comp;
693-
}
694-
}
695-
return 0;
696-
}
697-
698-
private static int validateResourceVersion(String v1) {
699-
int v1Length = v1.length();
700-
if (v1Length == 0) {
701-
throw new NonComparableResourceVersionException("Resource version is empty");
702-
}
703-
for (int i = 0; i < v1Length; i++) {
704-
char char1 = v1.charAt(i);
705-
if (char1 == '0') {
706-
if (i == 0) {
707-
throw new NonComparableResourceVersionException(
708-
"Resource version cannot begin with 0: " + v1);
709-
}
710-
} else if (char1 < '0' || char1 > '9') {
711-
throw new NonComparableResourceVersionException(
712-
"Non numeric characters in resource version: " + v1);
713-
}
714-
}
715-
return v1Length;
716-
}
717598
}

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/EventFilterDetails.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import java.util.Optional;
1919

20-
import io.javaoperatorsdk.operator.api.reconciler.ReconcileUtils;
20+
import io.javaoperatorsdk.operator.ReconcilerUtilsInternal;
2121
import io.javaoperatorsdk.operator.processing.event.source.controller.ResourceEvent;
2222

2323
class EventFilterDetails {
@@ -41,7 +41,7 @@ public void setLastEvent(ResourceEvent event) {
4141
public Optional<ResourceEvent> getLatestEventAfterLastUpdateEvent(String updatedResourceVersion) {
4242
if (lastEvent != null
4343
&& (updatedResourceVersion == null
44-
|| ReconcileUtils.compareResourceVersions(
44+
|| ReconcilerUtilsInternal.compareResourceVersions(
4545
lastEvent.getResource().orElseThrow().getMetadata().getResourceVersion(),
4646
updatedResourceVersion)
4747
> 0)) {

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@
3232
import io.fabric8.kubernetes.client.dsl.MixedOperation;
3333
import io.fabric8.kubernetes.client.informers.ResourceEventHandler;
3434
import io.javaoperatorsdk.operator.OperatorException;
35+
import io.javaoperatorsdk.operator.ReconcilerUtilsInternal;
3536
import io.javaoperatorsdk.operator.api.config.ControllerConfiguration;
3637
import io.javaoperatorsdk.operator.api.config.Informable;
3738
import io.javaoperatorsdk.operator.api.config.NamespaceChangeable;
38-
import io.javaoperatorsdk.operator.api.reconciler.ReconcileUtils;
3939
import io.javaoperatorsdk.operator.api.reconciler.dependent.RecentOperationCacheFiller;
4040
import io.javaoperatorsdk.operator.health.InformerHealthIndicator;
4141
import io.javaoperatorsdk.operator.health.InformerWrappingEventSourceHealthIndicator;
@@ -181,7 +181,8 @@ public Optional<R> get(ResourceID resourceID) {
181181
Optional<R> resource = temporaryResourceCache.getResourceFromCache(resourceID);
182182
if (comparableResourceVersions
183183
&& resource.isPresent()
184-
&& res.filter(r -> ReconcileUtils.compareResourceVersions(r, resource.orElseThrow()) > 0)
184+
&& res.filter(
185+
r -> ReconcilerUtilsInternal.compareResourceVersions(r, resource.orElseThrow()) > 0)
185186
.isEmpty()) {
186187
log.debug("Latest resource found in temporary cache for Resource ID: {}", resourceID);
187188
return resource;

operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceCache.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import org.slf4j.LoggerFactory;
2525

2626
import io.fabric8.kubernetes.api.model.HasMetadata;
27-
import io.javaoperatorsdk.operator.api.reconciler.ReconcileUtils;
27+
import io.javaoperatorsdk.operator.ReconcilerUtilsInternal;
2828
import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource;
2929
import io.javaoperatorsdk.operator.processing.event.ResourceID;
3030
import io.javaoperatorsdk.operator.processing.event.source.ResourceAction;
@@ -128,7 +128,7 @@ private synchronized EventHandling onEvent(
128128
var cached = cache.get(resourceId);
129129
EventHandling result = EventHandling.NEW;
130130
if (cached != null) {
131-
int comp = ReconcileUtils.compareResourceVersions(resource, cached);
131+
int comp = ReconcilerUtilsInternal.compareResourceVersions(resource, cached);
132132
if (comp >= 0 || unknownState) {
133133
cache.remove(resourceId);
134134
// we propagate event only for our update or newer other can be discarded since we know we
@@ -174,7 +174,7 @@ public synchronized void putResource(T newResource) {
174174
// this also prevents resurrecting recently deleted entities for which the delete event
175175
// has already been processed
176176
if (latestResourceVersion != null
177-
&& ReconcileUtils.compareResourceVersions(
177+
&& ReconcilerUtilsInternal.compareResourceVersions(
178178
latestResourceVersion, newResource.getMetadata().getResourceVersion())
179179
> 0) {
180180
log.debug(
@@ -189,7 +189,7 @@ public synchronized void putResource(T newResource) {
189189
var cachedResource = getResourceFromCache(resourceId).orElse(null);
190190

191191
if (cachedResource == null
192-
|| ReconcileUtils.compareResourceVersions(newResource, cachedResource) > 0) {
192+
|| ReconcilerUtilsInternal.compareResourceVersions(newResource, cachedResource) > 0) {
193193
log.debug(
194194
"Temporarily moving ahead to target version {} for resource id: {}",
195195
newResource.getMetadata().getResourceVersion(),

0 commit comments

Comments
 (0)