Skip to content

Federated API discovery skips removal reconciliation when discoverAPI() returns empty/null, orphaning previously-imported APIs #5149

Description

@veejask-41

Description

Summary

In wso2/carbon-apimgt FederatedAPIDiscoveryRunner.java, the scheduled discovery tick only runs
reconciliation (processDiscoveredAPIs) when the current tick returns a
non-empty API list. When the third-party gateway reports zero APIs, the
else branch fires and reconciliation is skipped entirely — so the
diff-and-undeploy logic never executes and every previously-imported API is
left in place indefinitely.

Where reconciliation lives

processDiscoveredAPIs() (called from FederatedAPIDiscoveryRunner.java:145-146,
implemented at lines 190-368) does contain a real diff/reconcile mechanism:

  1. Fetches the set of APIs currently known as federated-discovered for that
    environment/org from the DB (alreadyDiscoveredAPIsList, via
    AM_DEPLOYMENT_REVISION_MAPPING / AM_REVISION / AM_API where
    INITIATED_FROM_GW=1).
  2. Walks the freshly discoverAPI()-returned list, importing/updating each one
    and tracking every key seen (discoveredAPIsFromFederatedGW).
  3. At lines 340-357, diffs the two: any API in alreadyDiscoveredAPIsList but
    not in discoveredAPIsFromFederatedGW gets
    FederatedGatewayUtil.deleteDeployment(...) called on it.

So per-API removal detection genuinely exists — but only inside this method,
and only when the current tick's discoveredAPIs is non-empty.

The bug

if (discoveredAPIs != null && !discoveredAPIs.isEmpty()) {
    ... processDiscoveredAPIs(...)   // reconcile runs
} else {
    log.debug(...)                    // reconcile is SKIPPED entirely
}

If the third-party gateway ends up with zero APIs (all deleted, gateway
decommissioned, or even a transient connectivity blip that makes discoverAPI()
return empty/null), the else branch fires and processDiscoveredAPIs never
runs — so the diff-and-undeploy logic at lines 340-357 never executes. Every
previously-imported API is left exactly as it was, indefinitely.

Impact

  • Orphaned/stale API entries remain published after the source gateway removes
    them or goes away.
  • A transient connectivity blip returning empty/null can be
    indistinguishable from a legitimate "all APIs removed" state, but is treated
    as a no-op regardless.

Expected behavior

Reconciliation should run even when discoverAPI() returns an empty list, so
that APIs removed at the source are undeployed. (Empty vs. null / error should
be distinguished so a transient failure does not mass-undeploy healthy APIs.)

Screenshots

1. Guard that skips reconciliation (FederatedAPIDiscoveryRunner.java)

Image

2. Discovered API left published in the UI (PetStore :1.0.0)

Image

Steps to Reproduce

Steps to reproduce

  1. Connect a third-party (federated) gateway to the environment/org.
  2. Wait for the scheduled discovery to run and import APIs — confirm at least
    one API shows up as a discovered/imported API (e.g. PetStore :1.0.0,
    "Discovered API - AWS", Published).
  3. On the third-party gateway, remove all the APIs in one go (so the next
    discoverAPI() call returns an empty/null list).
  4. Wait for the next scheduled discovery tick.

Expected result

Reconciliation runs, detects that all previously-discovered APIs are gone from
the source, and undeploys/removes them.

Actual result

Because discoveredAPIs is empty, the else branch fires and
processDiscoveredAPIs never runs. The diff-and-undeploy logic (lines 340-357)
never executes, and every previously-imported API remains published
indefinitely.

Version

4.7.0

Environment Details (with versions)

JDK: 21.0.11-tem
OS: MacOS
Third party gateway: AWS

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions