|
|
@@ -134,8 +134,9 @@ type AppConnector struct {
|
|
|
updatePub *eventbus.Publisher[appctype.RouteUpdate]
|
|
|
storePub *eventbus.Publisher[appctype.RouteInfo]
|
|
|
|
|
|
- // storeRoutesFunc will be called to persist routes if it is not nil.
|
|
|
- storeRoutesFunc func(*appctype.RouteInfo) error
|
|
|
+ // hasStoredRoutes records whether the connector was initialized with
|
|
|
+ // persisted route information.
|
|
|
+ hasStoredRoutes bool
|
|
|
|
|
|
// mu guards the fields that follow
|
|
|
mu sync.Mutex
|
|
|
@@ -168,16 +169,14 @@ type Config struct {
|
|
|
EventBus *eventbus.Bus
|
|
|
|
|
|
// RouteAdvertiser allows the connector to update the set of advertised routes.
|
|
|
- // It must be non-nil.
|
|
|
RouteAdvertiser RouteAdvertiser
|
|
|
|
|
|
// RouteInfo, if non-nil, use used as the initial set of routes for the
|
|
|
// connector. If nil, the connector starts empty.
|
|
|
RouteInfo *appctype.RouteInfo
|
|
|
|
|
|
- // StoreRoutesFunc, if non-nil, is called when the connector's routes
|
|
|
- // change, to allow the routes to be persisted.
|
|
|
- StoreRoutesFunc func(*appctype.RouteInfo) error
|
|
|
+ // HasStoredRoutes indicates that the connector should assume stored routes.
|
|
|
+ HasStoredRoutes bool
|
|
|
}
|
|
|
|
|
|
// NewAppConnector creates a new AppConnector.
|
|
|
@@ -187,8 +186,6 @@ func NewAppConnector(c Config) *AppConnector {
|
|
|
panic("missing logger")
|
|
|
case c.EventBus == nil:
|
|
|
panic("missing event bus")
|
|
|
- case c.RouteAdvertiser == nil:
|
|
|
- panic("missing route advertiser")
|
|
|
}
|
|
|
ec := c.EventBus.Client("appc.AppConnector")
|
|
|
|
|
|
@@ -199,7 +196,7 @@ func NewAppConnector(c Config) *AppConnector {
|
|
|
updatePub: eventbus.Publish[appctype.RouteUpdate](ec),
|
|
|
storePub: eventbus.Publish[appctype.RouteInfo](ec),
|
|
|
routeAdvertiser: c.RouteAdvertiser,
|
|
|
- storeRoutesFunc: c.StoreRoutesFunc,
|
|
|
+ hasStoredRoutes: c.HasStoredRoutes,
|
|
|
}
|
|
|
if c.RouteInfo != nil {
|
|
|
ac.domains = c.RouteInfo.Domains
|
|
|
@@ -218,13 +215,19 @@ func NewAppConnector(c Config) *AppConnector {
|
|
|
|
|
|
// ShouldStoreRoutes returns true if the appconnector was created with the controlknob on
|
|
|
// and is storing its discovered routes persistently.
|
|
|
-func (e *AppConnector) ShouldStoreRoutes() bool {
|
|
|
- return e.storeRoutesFunc != nil
|
|
|
-}
|
|
|
+func (e *AppConnector) ShouldStoreRoutes() bool { return e.hasStoredRoutes }
|
|
|
|
|
|
// storeRoutesLocked takes the current state of the AppConnector and persists it
|
|
|
-func (e *AppConnector) storeRoutesLocked() error {
|
|
|
+func (e *AppConnector) storeRoutesLocked() {
|
|
|
if e.storePub.ShouldPublish() {
|
|
|
+ // log write rate and write size
|
|
|
+ numRoutes := int64(len(e.controlRoutes))
|
|
|
+ for _, rs := range e.domains {
|
|
|
+ numRoutes += int64(len(rs))
|
|
|
+ }
|
|
|
+ e.writeRateMinute.update(numRoutes)
|
|
|
+ e.writeRateDay.update(numRoutes)
|
|
|
+
|
|
|
e.storePub.Publish(appctype.RouteInfo{
|
|
|
// Clone here, as the subscriber will handle these outside our lock.
|
|
|
Control: slices.Clone(e.controlRoutes),
|
|
|
@@ -232,24 +235,6 @@ func (e *AppConnector) storeRoutesLocked() error {
|
|
|
Wildcards: slices.Clone(e.wildcards),
|
|
|
})
|
|
|
}
|
|
|
- if !e.ShouldStoreRoutes() {
|
|
|
- return nil
|
|
|
- }
|
|
|
-
|
|
|
- // log write rate and write size
|
|
|
- numRoutes := int64(len(e.controlRoutes))
|
|
|
- for _, rs := range e.domains {
|
|
|
- numRoutes += int64(len(rs))
|
|
|
- }
|
|
|
- e.writeRateMinute.update(numRoutes)
|
|
|
- e.writeRateDay.update(numRoutes)
|
|
|
-
|
|
|
- // TODO(creachdair): Remove this once it's delivered over the event bus.
|
|
|
- return e.storeRoutesFunc(&appctype.RouteInfo{
|
|
|
- Control: e.controlRoutes,
|
|
|
- Domains: e.domains,
|
|
|
- Wildcards: e.wildcards,
|
|
|
- })
|
|
|
}
|
|
|
|
|
|
// ClearRoutes removes all route state from the AppConnector.
|
|
|
@@ -259,7 +244,8 @@ func (e *AppConnector) ClearRoutes() error {
|
|
|
e.controlRoutes = nil
|
|
|
e.domains = nil
|
|
|
e.wildcards = nil
|
|
|
- return e.storeRoutesLocked()
|
|
|
+ e.storeRoutesLocked()
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
// UpdateDomainsAndRoutes starts an asynchronous update of the configuration
|
|
|
@@ -331,9 +317,9 @@ func (e *AppConnector) updateDomains(domains []string) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Everything left in oldDomains is a domain we're no longer tracking
|
|
|
- // and if we are storing route info we can unadvertise the routes
|
|
|
- if e.ShouldStoreRoutes() {
|
|
|
+ // Everything left in oldDomains is a domain we're no longer tracking and we
|
|
|
+ // can unadvertise the routes.
|
|
|
+ if e.hasStoredRoutes {
|
|
|
toRemove := []netip.Prefix{}
|
|
|
for _, addrs := range oldDomains {
|
|
|
for _, a := range addrs {
|
|
|
@@ -342,11 +328,13 @@ func (e *AppConnector) updateDomains(domains []string) {
|
|
|
}
|
|
|
|
|
|
if len(toRemove) != 0 {
|
|
|
- e.queue.Add(func() {
|
|
|
- if err := e.routeAdvertiser.UnadvertiseRoute(toRemove...); err != nil {
|
|
|
- e.logf("failed to unadvertise routes on domain removal: %v: %v: %v", slicesx.MapKeys(oldDomains), toRemove, err)
|
|
|
- }
|
|
|
- })
|
|
|
+ if ra := e.routeAdvertiser; ra != nil {
|
|
|
+ e.queue.Add(func() {
|
|
|
+ if err := e.routeAdvertiser.UnadvertiseRoute(toRemove...); err != nil {
|
|
|
+ e.logf("failed to unadvertise routes on domain removal: %v: %v: %v", slicesx.MapKeys(oldDomains), toRemove, err)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
e.updatePub.Publish(appctype.RouteUpdate{Unadvertise: toRemove})
|
|
|
}
|
|
|
}
|
|
|
@@ -369,11 +357,10 @@ func (e *AppConnector) updateRoutes(routes []netip.Prefix) {
|
|
|
|
|
|
var toRemove []netip.Prefix
|
|
|
|
|
|
- // If we're storing routes and know e.controlRoutes is a good
|
|
|
- // representation of what should be in AdvertisedRoutes we can stop
|
|
|
- // advertising routes that used to be in e.controlRoutes but are not
|
|
|
- // in routes.
|
|
|
- if e.ShouldStoreRoutes() {
|
|
|
+ // If we know e.controlRoutes is a good representation of what should be in
|
|
|
+ // AdvertisedRoutes we can stop advertising routes that used to be in
|
|
|
+ // e.controlRoutes but are not in routes.
|
|
|
+ if e.hasStoredRoutes {
|
|
|
toRemove = routesWithout(e.controlRoutes, routes)
|
|
|
}
|
|
|
|
|
|
@@ -390,23 +377,23 @@ nextRoute:
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- e.queue.Add(func() {
|
|
|
- if err := e.routeAdvertiser.AdvertiseRoute(routes...); err != nil {
|
|
|
- e.logf("failed to advertise routes: %v: %v", routes, err)
|
|
|
- }
|
|
|
- if err := e.routeAdvertiser.UnadvertiseRoute(toRemove...); err != nil {
|
|
|
- e.logf("failed to unadvertise routes: %v: %v", toRemove, err)
|
|
|
- }
|
|
|
- })
|
|
|
+ if e.routeAdvertiser != nil {
|
|
|
+ e.queue.Add(func() {
|
|
|
+ if err := e.routeAdvertiser.AdvertiseRoute(routes...); err != nil {
|
|
|
+ e.logf("failed to advertise routes: %v: %v", routes, err)
|
|
|
+ }
|
|
|
+ if err := e.routeAdvertiser.UnadvertiseRoute(toRemove...); err != nil {
|
|
|
+ e.logf("failed to unadvertise routes: %v: %v", toRemove, err)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
e.updatePub.Publish(appctype.RouteUpdate{
|
|
|
Advertise: routes,
|
|
|
Unadvertise: toRemove,
|
|
|
})
|
|
|
|
|
|
e.controlRoutes = routes
|
|
|
- if err := e.storeRoutesLocked(); err != nil {
|
|
|
- e.logf("failed to store route info: %v", err)
|
|
|
- }
|
|
|
+ e.storeRoutesLocked()
|
|
|
}
|
|
|
|
|
|
// Domains returns the currently configured domain list.
|
|
|
@@ -485,9 +472,11 @@ func (e *AppConnector) isAddrKnownLocked(domain string, addr netip.Addr) bool {
|
|
|
// associated with the given domain.
|
|
|
func (e *AppConnector) scheduleAdvertisement(domain string, routes ...netip.Prefix) {
|
|
|
e.queue.Add(func() {
|
|
|
- if err := e.routeAdvertiser.AdvertiseRoute(routes...); err != nil {
|
|
|
- e.logf("failed to advertise routes for %s: %v: %v", domain, routes, err)
|
|
|
- return
|
|
|
+ if e.routeAdvertiser != nil {
|
|
|
+ if err := e.routeAdvertiser.AdvertiseRoute(routes...); err != nil {
|
|
|
+ e.logf("failed to advertise routes for %s: %v: %v", domain, routes, err)
|
|
|
+ return
|
|
|
+ }
|
|
|
}
|
|
|
e.updatePub.Publish(appctype.RouteUpdate{Advertise: routes})
|
|
|
e.mu.Lock()
|
|
|
@@ -503,9 +492,7 @@ func (e *AppConnector) scheduleAdvertisement(domain string, routes ...netip.Pref
|
|
|
e.logf("[v2] advertised route for %v: %v", domain, addr)
|
|
|
}
|
|
|
}
|
|
|
- if err := e.storeRoutesLocked(); err != nil {
|
|
|
- e.logf("failed to store route info: %v", err)
|
|
|
- }
|
|
|
+ e.storeRoutesLocked()
|
|
|
})
|
|
|
}
|
|
|
|