Lots of refactoring. We now have basic routing table watcher.
This commit is contained in:
parent
5899134b66
commit
338e0fdf18
@ -3,7 +3,9 @@ package router
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/micro/go-log"
|
||||||
"github.com/micro/go-micro/registry"
|
"github.com/micro/go-micro/registry"
|
||||||
"github.com/micro/go-micro/registry/gossip"
|
"github.com/micro/go-micro/registry/gossip"
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
@ -12,6 +14,8 @@ import (
|
|||||||
type router struct {
|
type router struct {
|
||||||
opts Options
|
opts Options
|
||||||
goss registry.Registry
|
goss registry.Registry
|
||||||
|
exit chan struct{}
|
||||||
|
wg *sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRouter(opts ...Option) Router {
|
func newRouter(opts ...Option) Router {
|
||||||
@ -20,22 +24,22 @@ func newRouter(opts ...Option) Router {
|
|||||||
Table: NewTable(),
|
Table: NewTable(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// apply requested options
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bind to gossip address to join gossip registry
|
||||||
goss := gossip.NewRegistry(
|
goss := gossip.NewRegistry(
|
||||||
gossip.Address(options.GossipAddr),
|
gossip.Address(options.GossipAddr),
|
||||||
)
|
)
|
||||||
|
|
||||||
r := &router{
|
return &router{
|
||||||
opts: options,
|
opts: options,
|
||||||
goss: goss,
|
goss: goss,
|
||||||
|
exit: make(chan struct{}),
|
||||||
|
wg: &sync.WaitGroup{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: start gossip.Registry watch here
|
|
||||||
|
|
||||||
return r
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes router with given options
|
// Init initializes router with given options
|
||||||
@ -66,6 +70,118 @@ func (r *router) Network() string {
|
|||||||
return r.opts.NetworkAddr
|
return r.opts.NetworkAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start starts the router
|
||||||
|
func (r *router) Start() error {
|
||||||
|
// TODO:
|
||||||
|
// - list all remote services and populate routing table
|
||||||
|
// - list all local services and populate remote registry
|
||||||
|
|
||||||
|
gWatcher, err := r.goss.Watch()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create router gossip registry watcher: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tWatcher, err := r.opts.Table.Watch()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create routing table watcher: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.wg.Add(1)
|
||||||
|
go r.watchGossip(gWatcher)
|
||||||
|
|
||||||
|
r.wg.Add(1)
|
||||||
|
go r.watchTable(tWatcher)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// watch gossip registry
|
||||||
|
func (r *router) watchGossip(w registry.Watcher) error {
|
||||||
|
defer r.wg.Done()
|
||||||
|
|
||||||
|
r.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer r.wg.Done()
|
||||||
|
<-r.exit
|
||||||
|
// stop gossip registry watcher
|
||||||
|
w.Stop()
|
||||||
|
}()
|
||||||
|
|
||||||
|
var watchErr error
|
||||||
|
|
||||||
|
// watch for changes to services
|
||||||
|
for {
|
||||||
|
res, err := w.Next()
|
||||||
|
if err == registry.ErrWatcherStopped {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
watchErr = err
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch res.Action {
|
||||||
|
case "create":
|
||||||
|
if len(res.Service.Nodes) > 0 {
|
||||||
|
log.Logf("Action: %s, Service: %v", res.Action, res.Service.Name)
|
||||||
|
}
|
||||||
|
case "delete":
|
||||||
|
log.Logf("Action: %s, Service: %v", res.Action, res.Service.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return watchErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// watch gossip registry
|
||||||
|
func (r *router) watchTable(w Watcher) error {
|
||||||
|
defer r.wg.Done()
|
||||||
|
|
||||||
|
r.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer r.wg.Done()
|
||||||
|
<-r.exit
|
||||||
|
// stop gossip registry watcher
|
||||||
|
w.Stop()
|
||||||
|
}()
|
||||||
|
|
||||||
|
var watchErr error
|
||||||
|
|
||||||
|
// watch for changes to services
|
||||||
|
for {
|
||||||
|
res, err := w.Next()
|
||||||
|
if err == ErrWatcherStopped {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
watchErr = err
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch res.Action {
|
||||||
|
case "add":
|
||||||
|
log.Logf("Action: %s, Route: %v", res.Action, res.Route)
|
||||||
|
case "remove":
|
||||||
|
log.Logf("Action: %s, Route: %v", res.Action, res.Route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return watchErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stops the router
|
||||||
|
func (r *router) Stop() error {
|
||||||
|
// notify all goroutines to finish
|
||||||
|
close(r.exit)
|
||||||
|
|
||||||
|
// wait for all goroutines to finish
|
||||||
|
r.wg.Wait()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// String prints debugging information about router
|
// String prints debugging information about router
|
||||||
func (r *router) String() string {
|
func (r *router) String() string {
|
||||||
sb := &strings.Builder{}
|
sb := &strings.Builder{}
|
||||||
|
@ -14,8 +14,8 @@ const (
|
|||||||
type RouteOptions struct {
|
type RouteOptions struct {
|
||||||
// DestAddr is destination address
|
// DestAddr is destination address
|
||||||
DestAddr string
|
DestAddr string
|
||||||
// Hop is the next route hop
|
// Gateway is the next route hop
|
||||||
Hop Router
|
Gateway Router
|
||||||
// Network defines micro network
|
// Network defines micro network
|
||||||
Network string
|
Network string
|
||||||
// Metric is route cost metric
|
// Metric is route cost metric
|
||||||
@ -31,10 +31,10 @@ func DestAddr(a string) RouteOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hop allows to set the route route options
|
// Gateway sets the route gateway
|
||||||
func Hop(r Router) RouteOption {
|
func Gateway(r Router) RouteOption {
|
||||||
return func(o *RouteOptions) {
|
return func(o *RouteOptions) {
|
||||||
o.Hop = r
|
o.Gateway = r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,21 +29,21 @@ func ID(id string) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Address allows to set router address
|
// Address sets router address
|
||||||
func Address(a string) Option {
|
func Address(a string) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.Address = a
|
o.Address = a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GossipAddress allows to set router address
|
// GossipAddr sets router gossip address
|
||||||
func GossipAddress(a string) Option {
|
func GossipAddr(a string) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.GossipAddr = a
|
o.GossipAddr = a
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkAddr allows to set router network
|
// NetworkAddr sets router network address
|
||||||
func NetworkAddr(n string) Option {
|
func NetworkAddr(n string) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.NetworkAddr = n
|
o.NetworkAddr = n
|
||||||
|
@ -13,26 +13,17 @@ const (
|
|||||||
// QueryOptions allow to define routing table query options
|
// QueryOptions allow to define routing table query options
|
||||||
type QueryOptions struct {
|
type QueryOptions struct {
|
||||||
// Route allows to set route options
|
// Route allows to set route options
|
||||||
Route *RouteOptions
|
RouteOptions *RouteOptions
|
||||||
// Service is micro service name
|
|
||||||
Service string
|
|
||||||
// Policy defines query lookup policy
|
// Policy defines query lookup policy
|
||||||
Policy LookupPolicy
|
Policy LookupPolicy
|
||||||
// Count defines max number of results to return
|
// Count defines max number of results to return
|
||||||
Count int
|
Count int
|
||||||
}
|
}
|
||||||
|
|
||||||
// RouteOpts allows to set the route query options
|
// QueryRouteOpts allows to set the route query options
|
||||||
func RouteOpts(r *RouteOptions) QueryOption {
|
func QueryRouteOptons(r *RouteOptions) QueryOption {
|
||||||
return func(o *QueryOptions) {
|
return func(o *QueryOptions) {
|
||||||
o.Route = r
|
o.RouteOptions = r
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Service allows to set the service name in routing query
|
|
||||||
func Service(s string) QueryOption {
|
|
||||||
return func(o *QueryOptions) {
|
|
||||||
o.Service = s
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,8 +34,8 @@ func QueryPolicy(p LookupPolicy) QueryOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResultCount allows to set max results to return
|
// QueryCount allows to set max results to return
|
||||||
func ResultCount(c int) QueryOption {
|
func QueryCount(c int) QueryOption {
|
||||||
return func(o *QueryOptions) {
|
return func(o *QueryOptions) {
|
||||||
o.Count = c
|
o.Count = c
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Package router provides an interface for micro network routers
|
// Package router provides an interface for micro network router
|
||||||
package router
|
package router
|
||||||
|
|
||||||
// Router is micro network router
|
// Router is micro network router
|
||||||
@ -9,11 +9,15 @@ type Router interface {
|
|||||||
Options() Options
|
Options() Options
|
||||||
// Table returns routing table
|
// Table returns routing table
|
||||||
Table() Table
|
Table() Table
|
||||||
// Address returns router gossip adddress
|
// Address returns router adddress
|
||||||
Address() string
|
Address() string
|
||||||
// Network returns micro network address
|
// Network returns router network address
|
||||||
Network() string
|
Network() string
|
||||||
// String implemens fmt.Stringer interface
|
// Start starts router
|
||||||
|
Start() error
|
||||||
|
// Stop stops router
|
||||||
|
Stop() error
|
||||||
|
// String returns router debug info
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,6 +36,9 @@ type RouteOption func(*RouteOptions)
|
|||||||
// QueryOption is used to define query options
|
// QueryOption is used to define query options
|
||||||
type QueryOption func(*QueryOptions)
|
type QueryOption func(*QueryOptions)
|
||||||
|
|
||||||
|
// WatchOption is used to define what routes to watch in the table
|
||||||
|
type WatchOption func(*WatchOptions)
|
||||||
|
|
||||||
// NewRouter creates new Router and returns it
|
// NewRouter creates new Router and returns it
|
||||||
func NewRouter(opts ...Option) Router {
|
func NewRouter(opts ...Option) Router {
|
||||||
return newRouter(opts...)
|
return newRouter(opts...)
|
||||||
|
115
router/table.go
115
router/table.go
@ -8,6 +8,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -24,12 +25,14 @@ var (
|
|||||||
type Table interface {
|
type Table interface {
|
||||||
// Add adds new route to the table
|
// Add adds new route to the table
|
||||||
Add(Route) error
|
Add(Route) error
|
||||||
// Remove removes route from the table
|
// Remove removes existing route from the table
|
||||||
Remove(Route) error
|
Remove(Route) error
|
||||||
// Update updates route in the table
|
// Update updates route in the table
|
||||||
Update(...RouteOption) error
|
Update(...RouteOption) error
|
||||||
// Lookup looks up routes in the table
|
// Lookup looks up routes in the table
|
||||||
Lookup(Query) ([]Route, error)
|
Lookup(Query) ([]Route, error)
|
||||||
|
// Watch returns a watcher which allows you to track updates to the table
|
||||||
|
Watch(opts ...WatchOption) (Watcher, error)
|
||||||
// Size returns the size of the table
|
// Size returns the size of the table
|
||||||
Size() int
|
Size() int
|
||||||
// String prints the routing table
|
// String prints the routing table
|
||||||
@ -37,12 +40,13 @@ type Table interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// table is routing table
|
// table is routing table
|
||||||
// It maps service name to routes
|
|
||||||
type table struct {
|
type table struct {
|
||||||
// m stores routing table map
|
// m stores routing table map
|
||||||
m map[uint64]Route
|
m map[string]map[uint64]Route
|
||||||
// h is a hasher hashes route entries
|
// h hashes route entries
|
||||||
h hash.Hash64
|
h hash.Hash64
|
||||||
|
// w is a list of table watchers
|
||||||
|
w map[string]*tableWatcher
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,73 +56,120 @@ func NewTable() Table {
|
|||||||
h.Reset()
|
h.Reset()
|
||||||
|
|
||||||
return &table{
|
return &table{
|
||||||
m: make(map[uint64]Route),
|
m: make(map[string]map[uint64]Route),
|
||||||
|
w: make(map[string]*tableWatcher),
|
||||||
h: h,
|
h: h,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add adds new routing entry
|
// Add adds a route to the routing table
|
||||||
func (t *table) Add(r Route) error {
|
func (t *table) Add(r Route) error {
|
||||||
t.Lock()
|
t.Lock()
|
||||||
defer t.Unlock()
|
defer t.Unlock()
|
||||||
|
|
||||||
|
destAddr := r.Options().DestAddr
|
||||||
sum := t.hash(r)
|
sum := t.hash(r)
|
||||||
|
|
||||||
if _, ok := t.m[sum]; !ok {
|
if _, ok := t.m[destAddr]; !ok {
|
||||||
t.m[sum] = r
|
t.m[destAddr] = make(map[uint64]Route)
|
||||||
|
t.m[destAddr][sum] = r
|
||||||
|
go t.sendResult(&Result{Action: "add", Route: r})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := t.m[sum]; ok && r.Options().Policy == OverrideIfExists {
|
if _, ok := t.m[destAddr][sum]; ok && r.Options().Policy == OverrideIfExists {
|
||||||
t.m[sum] = r
|
t.m[destAddr][sum] = r
|
||||||
|
go t.sendResult(&Result{Action: "update", Route: r})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrDuplicateRoute
|
return ErrDuplicateRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes entry from the routing table
|
// Remove removes the route from the routing table
|
||||||
func (t *table) Remove(r Route) error {
|
func (t *table) Remove(r Route) error {
|
||||||
t.Lock()
|
t.Lock()
|
||||||
defer t.Unlock()
|
defer t.Unlock()
|
||||||
|
|
||||||
|
destAddr := r.Options().DestAddr
|
||||||
sum := t.hash(r)
|
sum := t.hash(r)
|
||||||
|
|
||||||
if _, ok := t.m[sum]; !ok {
|
if _, ok := t.m[destAddr]; !ok {
|
||||||
return ErrRouteNotFound
|
return ErrRouteNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(t.m, sum)
|
delete(t.m[destAddr], sum)
|
||||||
|
go t.sendResult(&Result{Action: "remove", Route: r})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update updates routing entry
|
// Update updates routing table using propvided options
|
||||||
func (t *table) Update(opts ...RouteOption) error {
|
func (t *table) Update(opts ...RouteOption) error {
|
||||||
t.Lock()
|
t.Lock()
|
||||||
defer t.Unlock()
|
defer t.Unlock()
|
||||||
|
|
||||||
r := NewRoute(opts...)
|
r := NewRoute(opts...)
|
||||||
|
|
||||||
|
destAddr := r.Options().DestAddr
|
||||||
sum := t.hash(r)
|
sum := t.hash(r)
|
||||||
|
|
||||||
if _, ok := t.m[sum]; !ok {
|
if _, ok := t.m[destAddr]; !ok {
|
||||||
return ErrRouteNotFound
|
return ErrRouteNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := t.m[sum]; ok {
|
if _, ok := t.m[destAddr][sum]; ok {
|
||||||
t.m[sum] = r
|
t.m[destAddr][sum] = r
|
||||||
|
go t.sendResult(&Result{Action: "update", Route: r})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrRouteNotFound
|
return ErrRouteNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lookup looks up entry in the routing table
|
// Lookup queries routing table and returns all routes that match it
|
||||||
func (t *table) Lookup(q Query) ([]Route, error) {
|
func (t *table) Lookup(q Query) ([]Route, error) {
|
||||||
return nil, ErrNotImplemented
|
return nil, ErrNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Watch returns routing table entry watcher
|
||||||
|
func (t *table) Watch(opts ...WatchOption) (Watcher, error) {
|
||||||
|
// by default watch everything
|
||||||
|
wopts := WatchOptions{
|
||||||
|
DestAddr: "*",
|
||||||
|
Network: "*",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&wopts)
|
||||||
|
}
|
||||||
|
|
||||||
|
watcher := &tableWatcher{
|
||||||
|
opts: wopts,
|
||||||
|
resChan: make(chan *Result, 10),
|
||||||
|
done: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Lock()
|
||||||
|
t.w[uuid.New().String()] = watcher
|
||||||
|
t.Unlock()
|
||||||
|
|
||||||
|
return watcher, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// sendResult sends rules to all subscribe watchers
|
||||||
|
func (t *table) sendResult(r *Result) {
|
||||||
|
t.RLock()
|
||||||
|
defer t.RUnlock()
|
||||||
|
|
||||||
|
for _, w := range t.w {
|
||||||
|
select {
|
||||||
|
case w.resChan <- r:
|
||||||
|
case <-w.done:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Size returns the size of the routing table
|
// Size returns the size of the routing table
|
||||||
func (t *table) Size() int {
|
func (t *table) Size() int {
|
||||||
t.RLock()
|
t.RLock()
|
||||||
@ -127,7 +178,7 @@ func (t *table) Size() int {
|
|||||||
return len(t.m)
|
return len(t.m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns text representation of routing table
|
// String returns debug information
|
||||||
func (t *table) String() string {
|
func (t *table) String() string {
|
||||||
t.RLock()
|
t.RLock()
|
||||||
defer t.RUnlock()
|
defer t.RUnlock()
|
||||||
@ -137,16 +188,18 @@ func (t *table) String() string {
|
|||||||
|
|
||||||
// create nice table printing structure
|
// create nice table printing structure
|
||||||
table := tablewriter.NewWriter(sb)
|
table := tablewriter.NewWriter(sb)
|
||||||
table.SetHeader([]string{"Service", "Gateway", "Network", "Metric"})
|
table.SetHeader([]string{"Destination", "Gateway", "Network", "Metric"})
|
||||||
|
|
||||||
for _, route := range t.m {
|
for _, destRoute := range t.m {
|
||||||
strRoute := []string{
|
for _, route := range destRoute {
|
||||||
route.Options().DestAddr,
|
strRoute := []string{
|
||||||
route.Options().Hop.Address(),
|
route.Options().DestAddr,
|
||||||
route.Options().Network,
|
route.Options().Gateway.Address(),
|
||||||
fmt.Sprintf("%d", route.Options().Metric),
|
route.Options().Gateway.Network(),
|
||||||
|
fmt.Sprintf("%d", route.Options().Metric),
|
||||||
|
}
|
||||||
|
table.Append(strRoute)
|
||||||
}
|
}
|
||||||
table.Append(strRoute)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// render table into sb
|
// render table into sb
|
||||||
@ -155,13 +208,13 @@ func (t *table) String() string {
|
|||||||
return sb.String()
|
return sb.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hash hashes the route using router gateway and network address
|
||||||
func (t *table) hash(r Route) uint64 {
|
func (t *table) hash(r Route) uint64 {
|
||||||
destAddr := r.Options().DestAddr
|
gwAddr := r.Options().Gateway.Address()
|
||||||
routerAddr := r.Options().Hop.Address()
|
netAddr := r.Options().Network
|
||||||
network := r.Options().Network
|
|
||||||
|
|
||||||
t.h.Reset()
|
t.h.Reset()
|
||||||
t.h.Write([]byte(destAddr + routerAddr + network))
|
t.h.Write([]byte(gwAddr + netAddr))
|
||||||
|
|
||||||
return t.h.Sum64()
|
return t.h.Sum64()
|
||||||
}
|
}
|
||||||
|
89
router/table_watcher.go
Normal file
89
router/table_watcher.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrWatcherStopped is returned when routing table watcher has been stopped
|
||||||
|
ErrWatcherStopped = errors.New("routing table watcher stopped")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Watcher is an interface that returns updates to the routing table
|
||||||
|
type Watcher interface {
|
||||||
|
// Next is a blocking call that returns watch result
|
||||||
|
Next() (*Result, error)
|
||||||
|
// Stop stops watcher
|
||||||
|
Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result is returned by a call to Next on the watcher.
|
||||||
|
type Result struct {
|
||||||
|
// Action is routing table action which is either of add, remove or update
|
||||||
|
Action string
|
||||||
|
// Route is table rout
|
||||||
|
Route Route
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watcher options
|
||||||
|
type WatchOptions struct {
|
||||||
|
// Specify destination address to watch
|
||||||
|
DestAddr string
|
||||||
|
// Specify network to watch
|
||||||
|
Network string
|
||||||
|
}
|
||||||
|
|
||||||
|
// WatchDestAddr sets what destination to watch
|
||||||
|
// Destination is usually microservice name
|
||||||
|
func WatchDestAddr(a string) WatchOption {
|
||||||
|
return func(o *WatchOptions) {
|
||||||
|
o.DestAddr = a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WatchNetwork sets what network to watch
|
||||||
|
func WatchNetwork(n string) WatchOption {
|
||||||
|
return func(o *WatchOptions) {
|
||||||
|
o.Network = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type tableWatcher struct {
|
||||||
|
opts WatchOptions
|
||||||
|
resChan chan *Result
|
||||||
|
done chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: We might simply use Query here once QueryLookup is figured out
|
||||||
|
// Next returns the next noticed action taken on table
|
||||||
|
func (w *tableWatcher) Next() (*Result, error) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case res := <-w.resChan:
|
||||||
|
switch w.opts.DestAddr {
|
||||||
|
case "*":
|
||||||
|
if w.opts.Network == "*" || w.opts.Network == res.Route.Options().Network {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
case res.Route.Options().DestAddr:
|
||||||
|
if w.opts.Network == "*" || w.opts.Network == res.Route.Options().Network {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ignore if no match is found
|
||||||
|
continue
|
||||||
|
case <-w.done:
|
||||||
|
return nil, ErrWatcherStopped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop stops routing table watcher
|
||||||
|
func (w *tableWatcher) Stop() {
|
||||||
|
select {
|
||||||
|
case <-w.done:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
close(w.done)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user