Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: operator-framework/java-operator-sdk
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: operator-framework/java-operator-sdk
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: next
Choose a head ref
Checking mergeability… Don’t worry, you can still create the pull request.
  • 18 commits
  • 159 files changed
  • 4 contributors

Commits on Aug 28, 2026

  1. chore: set next version to 999-SNAPSHOT (#3469)

    Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
    csviri committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    a04024a View commit details
    Browse the repository at this point in the history
  2. improve: integration test to showcase external resource state in stat…

    …us (#3480)
    
    Add integration tests that showcases handling explicit state in status. Both for simple managed/secondary resources and using a dependent resource.
    
    
    Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
    Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
    csviri and Copilot committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    cef3be3 View commit details
    Browse the repository at this point in the history
  3. Informer pools (#3325)

    * feat: pool informers so controllers and event sources can share them
    
    Every InformerEventSource used to create its own SharedIndexInformer, so an
    operator whose controllers all watch the same secondary type - ConfigMap and
    Secret being the usual suspects - opened one watch connection and kept one cache
    per controller for the very same resources.
    
    Informers are now handed out by an InformerPool obtained from the
    ConfigurationService, keyed by an InformerClassifier. Event sources whose
    classifiers are equal are backed by one informer; the pool reference counts its
    users and stops the informer once the last one releases it. The classifier is
    made up of everything that decides what an informer watches and how: the
    KubernetesClient instance (compared by identity, since two clients for the same
    API server may still differ in credentials, impersonation or TLS material), the
    resource class or the group/version/kind for generic resources, the namespace,
    the label, field and shard selectors, and the item store.
    
    Two components are deliberately not part of that identity. The informerListLimit
    is excluded, so event sources that disagree only on it still share an informer,
    keeping the limit of whichever one created it and logging a warning. Indexers are
    excluded because they can be added to a running informer: they are registered
    under a name qualified with the controller and event source that added them, so
    index names stay private to an event source while callers keep using their own
    names, and they are removed again when that event source releases the informer.
    
    Two strategies ship: DefaultInformerPool shares as described and is the default,
    NonSharingInformerPool creates a dedicated informer per event source for anyone
    wanting to opt out. Either is selected with
    ConfigurationServiceOverrider#withInformerPool, and a custom strategy extends
    AbstractInformerPool, which already creates the informers from a classifier,
    starts them and waits for their caches to sync, leaving the subclass only the
    question of whether and when an informer is shared.
    
    Consequently informer creation and startup moved out of InformerWrapper and
    InformerManager into the pool, InformerManager acquires and releases informers
    instead of owning them, and it removes its own event handler and indexers from an
    informer that keeps running for others. An event source registered dynamically
    against an already running shared informer needs no special handling: the client
    replays the cache contents to a newly added handler.
    
    Also in support of the above: ConfigurationService#informerPool, an
    InformerEventSource constructor that no longer needs an EventSourceContext (the
    one taking it is deprecated), the resource group/version/kind on
    InformerConfiguration, equality and toString on FieldSelector, and equality of
    GroupVersionKindPlural made consistent with its hashCode so that an unspecified
    plural no longer splits informers.
    
    The pooling itself is production ready; the configuration API around it is marked
    experimental and may still change. Covered by unit tests for the pools, the
    classifier, the wrapper and the manager, and by integration tests for sharing,
    dynamic registration and de-registration that each run against both strategies.
    
    
    Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
    csviri committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    e179410 View commit details
    Browse the repository at this point in the history
  4. fix: new kotlin sample parent version

    This needed to be fixed after rebase.
    
    Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
    csviri committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    aacb3c5 View commit details
    Browse the repository at this point in the history
  5. fix: set configuration service for default pool (#3535)

    This is expected for a pool to set. Event if the underlying implementation would set it, this is the correct way to handle it.
    
    Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
    csviri committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    05ddbcc View commit details
    Browse the repository at this point in the history
  6. perf: size the workflow result map from Workflow#size (#3547)

    AbstractWorkflowExecutor called Workflow#getDependentResourcesByName purely to
    read its size. That allocates a HashMap and walks every node to collect the
    dependent resources, then discards the map. The executor is constructed on
    every reconcile and cleanup of a workflow-based reconciler, so use the existing
    Workflow#size instead.
    csviri committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    8ab41a3 View commit details
    Browse the repository at this point in the history
  7. refactor: call existing helpers instead of re-implementing them (#3544)

    No behavior change; each site is replaced by a helper that already exists.
    
    - PrimaryUpdateAndCacheUtils#compareResourceVersions (and its private
      validateResourceVersion) duplicated the entire algorithm of
      ReconcilerUtilsInternal#validateAndCompareResourceVersions: the length-first
      compare, the empty check, the leading-zero check and the same exception
      messages. It had no production caller, so the copy could silently drift from
      the version all production paths use. Delegate instead.
    - addFinalizerWithSSA builds its bare SSA skeleton with
      HasMetadata#initNameAndNamespaceFrom, which ResourceOperations already uses
      for the same purpose and which is Namespaced-aware.
    - AbstractInformerPool formats the informer identifier with
      ReconcilerUtilsInternal#getResourceTypeNameWithVersion instead of
      concatenating the resource name and version by hand.
    - EventFilterWindow uses ExtendedResourceEvent#getResourceVersion, which had
      no callers even though it is exactly the expression used here.
    - LocallyRunOperatorExtension instantiates reconcilers with Utils#instantiate,
      so it also supports non-public no-arg constructors and reports the failing
      class instead of wrapping in a bare RuntimeException.
    csviri committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    8bdfe58 View commit details
    Browse the repository at this point in the history
  8. test: fix flaky finalizer removal in TriggerReconcilerOnAllEventIT (#…

    …3543)
    
    The event count is increased at the beginning of the reconciliation, thus
    waiting for it released the test into the middle of a reconciliation that
    was still about to remove the finalizer. The subsequent update then raced
    with that removal and failed with a conflict.
    
    Wait for the finalizer removal to actually land, and retry the (optimistically
    locked) update with a fresh read on conflict.
    csviri committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    8a18799 View commit details
    Browse the repository at this point in the history
  9. feat: detect dependent resource API version changes (#3536)

    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.
    
    Guard against a null or immutable annotations map (e.g. Map.of(...))
    on the desired resource when detectApiVersionChange is enabled, since
    writing the last-applied-api-version marker (and the pre-existing
    previous-annotation bookkeeping that runs alongside it) requires a
    mutable map. Also rename a misleadingly-named test helper and replace
    a no-op assertion on a primitive boolean with a concrete expectation.
    
    Addresses Copilot review feedback on PR #3536.
    
    Add DetectApiVersionChangeIT covering the end-to-end scenario: a
    ConfigMap dependent resource configured with detectApiVersionChange
    is marked with the current API version on creation (without
    triggering an update), and a stale marker annotation left on the
    actual resource (simulating an older operator/CRD version) is
    detected and corrected with exactly one update, after which no
    further reconciliation loop occurs.
    
    Signed-off-by: hej090224 <fc49854985@gmail.com>
    Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
    2 people authored and csviri committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    2740cf6 View commit details
    Browse the repository at this point in the history
  10. refactor: resolve the informer target client without a downcast (#3548)

    InformerManager is generic over C extends Informable, but getTargetClient
    type-tested for InformerEventSourceConfiguration to find out whether a specific
    (e.g. remote cluster) client was configured. Informable has two implementors
    and only one could answer the question, so a third configuration type wanting
    its own client would be ignored silently rather than failing to compile.
    
    Move the default getKubernetesClient() up from InformerEventSourceConfiguration
    to Informable and let InformerManager ask the configuration directly. The
    default still returns Optional.empty(), so existing implementations are
    unaffected. As a side effect the ConfigurationService client is now only
    created when no specific client is configured, instead of being created and
    then discarded.
    csviri committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    0f576f2 View commit details
    Browse the repository at this point in the history
  11. refactor: clean up the Kubernetes resource matchers (#3546)

    - GenericKubernetesResourceMatcher allocated the path-prefix lists
      (List.of(SPEC), List.of(METADATA), the labels/annotations pair, List.of(STATUS))
      once per JSON-diff node while matching, and nodeIsChildOf built a stream per
      call. Both run for every node of every match, so hoist the lists to constants
      and use an indexed loop.
    - SSABasedGenericKubernetesResourceMatcher#sanitizeState nested the StatefulSet
      volume-claim-template handling four levels deep inside the type ladder;
      extract it into sanitizeVolumeClaimTemplates so the ladder reads as one
      dispatch per resource kind.
    csviri committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    8329795 View commit details
    Browse the repository at this point in the history
  12. improve: followup PR for Informer Pools (#3541)

    - addresses deprecations
    - addresses late PR comments for Informer Pools: #3325
    
    Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
    
    * Potential fix for pull request finding
    
    Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
    
    ---------
    
    Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
    Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
    csviri and Copilot committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    f2a5748 View commit details
    Browse the repository at this point in the history
  13. refactor: let ResourceState own the trigger-on-all-events flag (#3549)

    markEventReceived and unMarkEventReceived took a boolean that only ever selects
    which IllegalStateException guards apply, and every EventProcessor call site
    supplied it by re-reading the same controller configuration value. It can never
    differ between calls for a given processor, but nothing enforced that: a call
    site passing the wrong value would silently change which state transitions are
    legal, and the state machine could not be read without also reading its callers.
    
    Decide it once at the edge: ResourceStateManager takes the flag at construction
    (EventProcessor already knows it there) and passes it to each ResourceState,
    which keeps it as a final field. Both types are package-private, so this is
    self-contained.
    csviri committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    7504c22 View commit details
    Browse the repository at this point in the history
  14. perf: avoid redundant work on informer event paths (#3545)

    - Mappers#fromMetadata resolved the primary GroupVersionKind on every
      secondary event, although it only depends on the primary type. Hoist it out
      of the lambda and compare the encoded string before falling back to parsing
      the annotation value.
    - Mappers.SecondaryToPrimaryFromDefaultAnnotation built a whole new mapper on
      every invocation; hold a single delegate instead. The primaryResourceType
      field becomes unused and is dropped.
    - InformerEventSource#start walked the entire informer cache to seed the
      primary-to-secondary index even when that index is the no-op implementation
      (i.e. whenever a primaryToSecondaryMapper is configured), which is pure
      startup latency proportional to the number of cached secondaries.
    - ExternalResourceCachingEventSource#getSecondaryResources looked the primary
      up in the cache a second time although the value was already in a local.
    - PerResourcePollingEventSource#getAndCacheResource derived the same
      ResourceID twice in adjacent statements.
    csviri committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    9065185 View commit details
    Browse the repository at this point in the history
  15. improve: log informer re-use on info level (#3569)

    Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
    csviri committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    9a53fbd View commit details
    Browse the repository at this point in the history
  16. test: cover informer retry after a CR deserialization problem (#3558)

    Adds an IT for the situation where the resource that cannot be
    deserialized is already present when the informer lists on startup, the
    counterpart of MultiVersionCRDIT, which covers the watch case. With
    stopOnInformerErrorDuringStartup set to false the operator starts, but the
    informer is not retried: the exception handler installed by the informer pool
    declines a retry for deserialization errors and the Reflector of the fabric8
    client then completes its stop future, so fixing the problem in the cluster
    while the operator runs does not bring the informer back.
    
    The test asserts that current behavior, so it fails once the informer does get
    retried; the log of the informer pool no longer promises a periodic retry in
    this case.
    
    * Apply suggestions from code review
    
    Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
    csviri and Copilot committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    c5c8b16 View commit details
    Browse the repository at this point in the history
  17. fix: retain recently written external resources missing from a stale …

    …update (#3565)
    
    An update of the whole resource set of a primary (a poll result or a received
    event) might have been created before the reconciler wrote a resource, thus not
    containing it yet. Since such updates are handled as the full actual state, the
    write was lost from the cache, and the next reconciliation created a duplicate
    of an already created resource or repeated an already executed update.
    
    Writes are now marked as unconfirmed and retained for the next update if it
    either does not contain the resource at all - the expected case for a create -
    or still contains a state that a write replaced. Every state replaced since the
    last update is kept, since the reconciler might write the same resource multiple
    times in between, and an update created before any of those writes is stale. Any
    other state is treated as a change made outside of the reconciler and accepted
    as actual. Marks are dropped on the first update, so a resource really deleted
    or changed meanwhile is not retained indefinitely.
    
    Also guards handleRecentResourceUpdate against a missing cache entry, and
    resolves the actual resources from the state resources in the external state
    bulk dependent integration test, which is the recommended approach for
    resources that take longer to become visible.
    csviri committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    5ef9d39 View commit details
    Browse the repository at this point in the history
  18. fix: small issue after rebase on main

    Signed-off-by: Attila Mészáros <a_meszaros@apple.com>
    csviri committed Aug 28, 2026
    Configuration menu
    Copy the full SHA
    f2e2ef0 View commit details
    Browse the repository at this point in the history
Loading