diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/AbstractEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/AbstractEventSource.java index eba4e7ac0a..8d9984db14 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/AbstractEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/AbstractEventSource.java @@ -2,7 +2,7 @@ public abstract class AbstractEventSource implements EventSource { - protected EventHandler eventHandler; + protected volatile EventHandler eventHandler; @Override public void setEventHandler(EventHandler eventHandler) { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/InformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/InformerEventSource.java index d9252bb4c6..64b4a8d753 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/InformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/internal/InformerEventSource.java @@ -4,6 +4,9 @@ import java.util.Set; import java.util.function.Function; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.informers.ResourceEventHandler; @@ -16,35 +19,42 @@ public class InformerEventSource extends AbstractEventSource { + private static final Logger log = LoggerFactory.getLogger(InformerEventSource.class); + private final SharedInformer sharedInformer; - private final Function> resourceToUIDs; + private final Function> resourceToCustomResourceIDSet; private final Function associatedWith; private final boolean skipUpdateEventPropagationIfNoChange; public InformerEventSource(SharedInformer sharedInformer, - Function> resourceToUIDs) { - this(sharedInformer, resourceToUIDs, null, true); + Function> resourceToCustomResourceIDSet) { + this(sharedInformer, resourceToCustomResourceIDSet, null, true); } public InformerEventSource(KubernetesClient client, Class type, - Function> resourceToUIDs) { - this(client, type, resourceToUIDs, false); + Function> resourceToCustomResourceIDSet) { + this(client, type, resourceToCustomResourceIDSet, false); } InformerEventSource(KubernetesClient client, Class type, - Function> resourceToUIDs, + Function> resourceToCustomResourceIDSet, boolean skipUpdateEventPropagationIfNoChange) { - this(client.informers().sharedIndexInformerFor(type, 0), resourceToUIDs, null, + this(client.informers().sharedIndexInformerFor(type, 0), resourceToCustomResourceIDSet, null, skipUpdateEventPropagationIfNoChange); } public InformerEventSource(SharedInformer sharedInformer, - Function> resourceToUIDs, + Function> resourceToCustomResourceIDSet, Function associatedWith, boolean skipUpdateEventPropagationIfNoChange) { this.sharedInformer = sharedInformer; - this.resourceToUIDs = resourceToUIDs; + this.resourceToCustomResourceIDSet = resourceToCustomResourceIDSet; this.skipUpdateEventPropagationIfNoChange = skipUpdateEventPropagationIfNoChange; + if (sharedInformer.isRunning()) { + log.warn( + "Informer is already running on event source creation, this is not desirable and may " + + "lead to non deterministic behavior."); + } this.associatedWith = Objects.requireNonNullElseGet(associatedWith, () -> cr -> { final var metadata = cr.getMetadata(); @@ -76,13 +86,20 @@ public void onDelete(T t, boolean b) { } private void propagateEvent(T object) { - var uids = resourceToUIDs.apply(object); + var uids = resourceToCustomResourceIDSet.apply(object); if (uids.isEmpty()) { return; } uids.forEach(uid -> { Event event = new Event(CustomResourceID.fromResource(object)); - this.eventHandler.handleEvent(event); + /* + * In fabric8 client for certain cases informers can be created on in a way that they are + * automatically started, what would cause a NullPointerException here, since an event might + * be received between creation and registration. + */ + if (eventHandler != null) { + this.eventHandler.handleEvent(event); + } }); }