Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,16 @@
package io.javaoperatorsdk.operator.api.reconciler;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -364,13 +372,13 @@ public static <R extends HasMetadata> R resourcePatch(
if (esList.isEmpty()) {
throw new IllegalStateException("No event source found for type: " + resource.getClass());
}
var es = esList.get(0);
if (esList.size() > 1) {
throw new IllegalStateException(
"Multiple event sources found for: "
+ resource.getClass()
+ " please provide the target event source");
log.warn(
"Multiple event sources found for type: {}, selecting first with name {}",
resource.getClass(),
es.name());
}
var es = esList.get(0);
if (es instanceof ManagedInformerEventSource mes) {
return resourcePatch(resource, updateOperation, mes);
} else {
Expand Down Expand Up @@ -714,4 +722,56 @@ private static int validateResourceVersion(String v1) {
}
return v1Length;
}

/**
* Returns a collector that deduplicates Kubernetes objects by keeping only the one with the
* latest metadata.resourceVersion for each unique name and namespace combination. The intended
* use case is for the rather rare setup when there are overlapping {@link
* io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource}s for a
* resource type.
*
* @param <T> the type of HasMetadata objects
* @return a collector that produces a collection of deduplicated Kubernetes objects
*/
public static <T extends HasMetadata> Collector<T, ?, Collection<T>> latestDistinct() {
return Collectors.collectingAndThen(latestDistinctToMap(), Map::values);
}

/**
* Returns a collector that deduplicates Kubernetes objects by keeping only the one with the
* latest metadata.resourceVersion for each unique name and namespace combination. The intended
* use case is for the rather rare setup when there are overlapping {@link
* io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource}s for a
* resource type.
*
* @param <T> the type of HasMetadata objects
* @return a collector that produces a List of deduplicated Kubernetes objects
*/
public static <T extends HasMetadata> Collector<T, ?, List<T>> latestDistinctList() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't get why this method or the set variant are needed or even useful. The semantic should be a Set since instances should be unique, no?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The Set variant is needed since we are covering the use case whern there are multiple event sources for same type without distinct set of resource watched, so we returning the latest of those.

I added List just for convinience. If somebody would prefer working above Lists, that is quite common I beleive, but you are right that the semantically Set is the correct one.

return Collectors.collectingAndThen(
latestDistinctToMap(), map -> new ArrayList<>(map.values()));
}

/**
* Returns a collector that deduplicates Kubernetes objects by keeping only the one with the
* latest metadata.resourceVersion for each unique name and namespace combination. The intended
* use case is for the rather rare setup when there are overlapping {@link
* io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource}s for a
* resource type.
*
* @param <T> the type of HasMetadata objects
* @return a collector that produces a Set of deduplicated Kubernetes objects
*/
public static <T extends HasMetadata> Collector<T, ?, Set<T>> latestDistinctSet() {
return Collectors.collectingAndThen(latestDistinctToMap(), map -> new HashSet<>(map.values()));
}

private static <T extends HasMetadata> Collector<T, ?, Map<ResourceID, T>> latestDistinctToMap() {
return Collectors.toMap(
resource ->
new ResourceID(resource.getMetadata().getName(), resource.getMetadata().getNamespace()),
resource -> resource,
(existing, replacement) ->
compareResourceVersions(existing, replacement) >= 0 ? existing : replacement);
Copy link
Collaborator Author

@csviri csviri Jan 27, 2026

Choose a reason for hiding this comment

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

cc @metacosm this is the point to keep of this PR, just to keep the latest

}
}
Loading