FM Release 2 baseline: FastAPI rewrite, OPG.04 E/WBI surface, OAuth2, SRM command loop
Closes #11, #13 (closed), #17, #18. Advances #12 (closed), #15, #19.
What this is
This is a complete refactor of the FM core specially to be released with Release 2, following GSMA OPG.04 v6 spec.
The legacy Connexion/Flask/MongoDB tree is untouched and still present; this adds src/federation_manager/ alongside it. Nothing in the old tree is wired to the new one (and it will be removed very soon).
Two federated FMs establish federation with each other over real OAuth2, and a partner's InstallApp travels all the way to a real SRM and back. Both are covered by tests that run against live infrastructure, not mocks.
Stack and structure (#11, #12 (closed))
- FastAPI, hexagonal layout mirroring OEG and SRM:
core/,domain/,application/,adapters/,api/. - Pydantic settings, pydantic models throughout,
create_app(lifespan)composition root. - PostgreSQL via SQLAlchemy 2 async. Tables:
partner_ops,federation_contexts,federation_agreements,routing_rules,federation_transactions. - RFC 7807
application/problem+jsonerrors, with OPG.04'sProblemDetailsdeclared on every E/WBI operation. oop-commonsdoes not yet exist, so commons and contract types are vendored per component, as OEG and SRM already do.
EWBI surface
Paths under /operatorplatform/federation/v1.
| Operation | Direction |
|---|---|
CreateFederation |
inbound and outbound |
GetFederationHealth |
inbound |
DeleteFederationDetails |
inbound |
InstallApp |
inbound |
APIForwarding |
outbound |
onInstanceStatusEvent |
outbound callback |
POST /internal/federation/outbound is the OEG hand-off (RD §L.3), scoped to subscriber-identifier routing per ADR-0044.
Authorization (#13 (closed), #18)
- Inbound: Bearer JWT validated against Keycloak's JWKS, checking issuer, expiry and the
fed-mgmtscope, with theazpclaim resolving the partner viapartner_ops. - Outbound: OAuth2 Client-Credentials per partner, token cached until near expiry, secret read from a mounted file so rotation needs no redeploy.
- Callbacks use
fed-mgmt-notif, per OPG.04 Sec. 9.5. - No component outside FM is involved. Each partner's token endpoint comes from its own
partner_opsrow.
Partner addressing (#17)
X-Partner-Api-Root is gone. The partner's EWBI root is partner_ops.base_url, and every call is scoped by a federation_contexts row holding the directed federationContextId. Multi-operator federation works because partner location is registered state, not per-request input.
SRM integration (#19)
InstallApp resolves the agreement's (appId, appVersion, flavourId) mapping to a local service_specification_id, writes the transaction pending, publishes command.srm.service.deploy, then marks it in_progress. A durable JetStream consumer on OOP_EVENTS finalises the transaction from event.srm.operation.completed and notifies the partner. FM calls no TF-SDK, writes no SRM tables, and duplicates no placement logic.
Idempotency-Key is honoured: a replay returns the original instance, and the same key with a different body is a 409.
GSMA swagger improvements (to be reported to GSMA?)
docs/ vendors GSMA's OpenAPI byte-for-byte and records five defects in an OpenAPI Overlay 1.0.0 document, each citing the OPG.04 clause that settles it: two unresolvable $ref placeholders, a misplaced required on LockUnlockApplicationZone, APIForwarding requiring fields it never defines, apiResponse referencing the wrong schema, unconditional required where Table 197 makes members conditional, and FederationHealthInfo.federationStatus pointing at the alarm State object instead of the Status enum. scripts/apply_overlay.py regenerates the rendered profile. The base artifact is never edited.
Tests
137 tests. Unit tests use in-memory fakes; integration tests need PostgreSQL, NATS and Keycloak from docker-compose.dev.yaml.
test_two_stack_federation.pyruns two FMs with two databases against one Keycloak: FM-A federates with FM-B over real client-credentials tokens and queries its health.test_fm_srm_loop.pydrivesInstallAppthrough a real SRM and asserts the completion event comes back. Skips unlessFM_SRM_URLanswers; seedocs/running-srm.md.
Spec changes made alongside this
In oop/architecture, branch fm-persistence-model:
X-Federation-Correlation-IDdropped from the E/WBI surface. OPG.04 defines no correlation header and a partner never stores or echoes one, so it gave no cross-operator traceability.federation_transactions.federation_correlation_idbecomesexternal_txn_id, holding OPG.04'stxnIdentifier.federation_contexts.statusenumerated from the OPG.04Statusenum plusterminated. There is no pending state:CreateFederationreturns the context id synchronously.InstallAppidentifier forms recorded:appInstIdentifieris the app-instance UUID in hex becauseInstanceIdentifierforbids hyphens, whilezoneInfo.zoneIdis SRM's zone UUID per ADR-0017.fm/persistence-model.mdpromoted and aligned.
Known gaps, deliberately not in this MR
/readyzand structured logging (REQ-FED-15, REQ-FED-17) are missing; #11 and #12 (closed) stay open until they land.shared_resource_cataloguesis not implemented, so catalogue sync (UC-FM-03) is absent.- No SRM capacity pre-flight (REQ-FED-09), no terminate path, no network capability (UC-FM-04), no inbound synchronous query (UC-FM-05), no admin API (UC-FM-06).
RenewFederationneeds expiry/renewal columns onfederation_contextsand a schema change; it is deliberately deferred rather than faked.- A failed partner callback is recorded, not retried.
FM_ALLOW_INSECURE_PARTNER_ENDPOINTS(default false) exists so local stacks without TLS can be tested. It must stay false outside development.