feat(bfd): emit BFD session events into WatchEvents (ADR-0067 PR3b)#253
Merged
Conversation
Completes the operator surface: the BFD actor now publishes state-change
events into the unified EventService.WatchEvents stream.
- actor: a new internal BfdRuntimeEvent { peer, old_state, new_state,
diagnostic } broadcast on every Action::StateChanged. The actor stays
decoupled from the gRPC proto (mirrors fib_runtime's FibRuntimeEvent);
spawn() gains an event_tx parameter.
- daemon: a bridge task converts BfdRuntimeEvent → proto BgpEvent (Up →
SESSION_UP, Down/AdminDown → SESSION_DOWN, Init → STATE_CHANGED; full
old/new states in the BfdSessionEvent payload) and feeds a broadcaster wired
into EventService via ServeConfig.bfd_events.
- event_service: a bfd_stream merged into WatchEvents; filters flip from
reject → accept for EVENT_CATEGORY_BFD and the BFD event types, with
wants_bfd_events / matches_bfd_event. BFD is opt-in (not in the default
route+session set), filterable by category, type, and peer.
Tests: WatchEvents BFD category/peer filtering + default-stream exclusion
(event_service), an Up-event broadcast assertion in the privileged netns test.
ADR-0067 step 3b marked shipped; CHANGELOG updated.
There was a problem hiding this comment.
Pull request overview
This PR completes ADR-0067 step 3b by emitting BFD session state-transition events from the BFD actor into the unified gRPC EventService.WatchEvents stream, including filter support for BFD category/type/peer.
Changes:
- Added
BfdRuntimeEventemission on BFDAction::StateChanged, and threaded an event broadcaster throughbfd_runtime::spawn(). - Bridged runtime BFD events into
proto::BgpEventin the daemon and wired the broadcaster intoServeConfig/EventService. - Enabled BFD category/type filters and merged a new BFD stream into
WatchEvents, with unit tests validating opt-in behavior and peer filtering.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/main.rs | Adds BFD runtime→proto event conversion, bridge task, and wires BFD event broadcaster into gRPC serve config. |
| src/bfd_runtime.rs | Introduces BfdRuntimeEvent, threads a broadcast sender through the actor, and emits on session state changes (plus test coverage). |
| crates/api/src/server.rs | Extends ServeConfig and listener wiring to pass an optional BFD event broadcaster into EventService. |
| crates/api/src/event_service/filters.rs | Adds BFD selection/matching logic and updates category/type parsing to accept BFD filters. |
| crates/api/src/event_service.rs | Adds a BFD source stream merged into WatchEvents and includes tests for BFD filtering and opt-in semantics. |
| docs/adr/0067-bfd-single-hop.md | Marks ADR-0067 step 3b as shipped. |
| CHANGELOG.md | Documents BFD event emission into WatchEvents and clarifies the prior “proto only” entry. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+184
to
+187
| /// Convert a BFD actor state-change event into a unified gRPC `BgpEvent` | ||
| /// (ADR-0067 step 3b). Up → `SESSION_UP`, any down (Down/`AdminDown`) → | ||
| /// `SESSION_DOWN`, otherwise `STATE_CHANGED`; the full old/new states are | ||
| /// always carried in the `BfdSessionEvent` payload. |
Comment on lines
+1526
to
+1530
| // Actor state-change events (ADR-0067 step 3b): the actor broadcasts | ||
| // BfdRuntimeEvent; a bridge converts each to a proto BgpEvent that | ||
| // EventService surfaces over WatchEvents. Both channels are created | ||
| // unconditionally so the EventService stream is long-lived even when no BFD | ||
| // sessions are configured (it just stays empty). |
Comment on lines
56
to
60
| fib_route_snapshot: FibRouteSnapshotFn, | ||
| dataplane_events: DataplaneEventBroadcaster, | ||
| dataplane_route_events: DataplaneRouteEventBroadcaster, | ||
| bfd_events: DataplaneRouteEventBroadcaster, | ||
| metrics: BgpMetrics, |
…t, BfdEventBroadcaster alias - Doc comment on the event converter names the actual proto types (BfdSessionUp/Down/StateChanged), not the BGP-session shorthand. - Clarify which BFD channel is long-lived (the proto bfd_bgp_event_tx in ServeConfig) vs the actor event channel (dropped when the actor doesn't start, ending the bridge). - Introduce a dedicated `BfdEventBroadcaster` type alias instead of reusing `DataplaneRouteEventBroadcaster` for the EventService bfd_events field + constructor param.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Completes the BFD operator surface (after the inspection PR #252): the actor now
streams session state-change events over the unified
EventService.WatchEvents.Narrowly scoped to event emission, as agreed.
In this PR
src/bfd_runtime.rs): a new internalBfdRuntimeEvent{ peer, old_state, new_state, diagnostic } broadcast on every
Action::StateChanged. The actor stays decoupled from the gRPC proto(mirrors
fib_runtime'sFibRuntimeEvent);spawn()gains anevent_tx.src/main.rs): a bridge task convertsBfdRuntimeEvent→proto::BgpEvent(Up →SESSION_UP, Down/AdminDown→SESSION_DOWN,Init →
STATE_CHANGED; full old/new states in theBfdSessionEventpayload) and feeds a broadcaster wired into
EventServiceviaServeConfig.bfd_events.bfd_streammerged intoWatchEvents; the filtermatrix flips from reject → accept for
EVENT_CATEGORY_BFDand the BFDevent types (
wants_bfd_events/matches_bfd_event). BFD is opt-in(not in the default route+session set), filterable by category, event type,
and peer address.
Testing
cargo fmt/clippy --workspace -D warnings/test --workspacegreen.event_servicetests: BFD category/peer filtering delivers the rightevent; BFD does not leak into the default route+session stream.
event (verified via the Docker harness).
ADR-0067 step 3b marked shipped; CHANGELOG updated. This completes the plan's
PR3 (surface = #252 + events = this). Next: PR4 (RFC 5882 BGP coupling).