Lots of refactoring. We now have basic routing table watcher.
This commit is contained in:
		| @@ -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) | ||||||
|  | 	} | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user