Updated error statements; Update ships list of events.
This commit is contained in:
parent
6bdc23a3aa
commit
ea872f6900
@ -2,6 +2,7 @@ package router
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -10,6 +11,15 @@ import (
|
|||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UpdateRoutePenalty penalises route updates
|
||||||
|
UpdateRoutePenalty = 500
|
||||||
|
// DeleteRoutePenalty penalises route deletes
|
||||||
|
DeleteRoutePenalty = 1000
|
||||||
|
// AdvertiseTick is time interval in which we advertise route updates
|
||||||
|
AdvertiseTick = 5 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
// router provides default router implementation
|
// router provides default router implementation
|
||||||
type router struct {
|
type router struct {
|
||||||
opts Options
|
opts Options
|
||||||
@ -79,7 +89,7 @@ func (r *router) Network() string {
|
|||||||
func (r *router) addServiceRoutes(reg registry.Registry, network string, metric int) error {
|
func (r *router) addServiceRoutes(reg registry.Registry, network string, metric int) error {
|
||||||
services, err := reg.ListServices()
|
services, err := reg.ListServices()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to list services: %v", err)
|
return fmt.Errorf("failed listing services: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// add each service node as a separate route
|
// add each service node as a separate route
|
||||||
@ -148,12 +158,12 @@ func (r *router) manageServiceRoutes(w registry.Watcher, metric int) error {
|
|||||||
case "create":
|
case "create":
|
||||||
// only return error if the route is not duplicate, but something else has failed
|
// only return error if the route is not duplicate, but something else has failed
|
||||||
if err := r.opts.Table.Add(route); err != nil && err != ErrDuplicateRoute {
|
if err := r.opts.Table.Add(route); err != nil && err != ErrDuplicateRoute {
|
||||||
return fmt.Errorf("failed to add route for service %v: %s", res.Service.Name, err)
|
return fmt.Errorf("failed adding route for service %v: %s", res.Service.Name, err)
|
||||||
}
|
}
|
||||||
case "delete":
|
case "delete":
|
||||||
// only return error if the route is not in the table, but something else has failed
|
// only return error if the route is not in the table, but something else has failed
|
||||||
if err := r.opts.Table.Delete(route); err != nil && err != ErrRouteNotFound {
|
if err := r.opts.Table.Delete(route); err != nil && err != ErrRouteNotFound {
|
||||||
return fmt.Errorf("failed to delete route for service %v: %s", res.Service.Name, err)
|
return fmt.Errorf("failed adding route for service %v: %s", res.Service.Name, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,7 +185,6 @@ func (r *router) watchTable(w Watcher) error {
|
|||||||
|
|
||||||
var watchErr error
|
var watchErr error
|
||||||
|
|
||||||
exit:
|
|
||||||
for {
|
for {
|
||||||
event, err := w.Next()
|
event, err := w.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -188,12 +197,13 @@ exit:
|
|||||||
u := &Update{
|
u := &Update{
|
||||||
ID: r.ID(),
|
ID: r.ID(),
|
||||||
Timestamp: time.Now(),
|
Timestamp: time.Now(),
|
||||||
Event: event,
|
Events: []*Event{event},
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-r.exit:
|
case <-r.exit:
|
||||||
break exit
|
close(r.advertChan)
|
||||||
|
return watchErr
|
||||||
case r.advertChan <- u:
|
case r.advertChan <- u:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,7 +268,7 @@ func (r *router) Advertise() (<-chan *Update, error) {
|
|||||||
Metric: DefaultLocalMetric,
|
Metric: DefaultLocalMetric,
|
||||||
}
|
}
|
||||||
if err := r.opts.Table.Add(route); err != nil {
|
if err := r.opts.Table.Add(route); err != nil {
|
||||||
return nil, fmt.Errorf("error to add default gateway route: %s", err)
|
return nil, fmt.Errorf("failed adding default gateway route: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,12 +281,12 @@ func (r *router) Advertise() (<-chan *Update, error) {
|
|||||||
// routing table watcher which watches all routes i.e. to every destination
|
// routing table watcher which watches all routes i.e. to every destination
|
||||||
tableWatcher, err := r.opts.Table.Watch(WatchDestination("*"))
|
tableWatcher, err := r.opts.Table.Watch(WatchDestination("*"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create routing table watcher: %v", err)
|
return nil, fmt.Errorf("failed creating routing table watcher: %v", err)
|
||||||
}
|
}
|
||||||
// registry watcher
|
// registry watcher
|
||||||
regWatcher, err := r.opts.Registry.Watch()
|
regWatcher, err := r.opts.Registry.Watch()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create registry watcher: %v", err)
|
return nil, fmt.Errorf("failed creating registry watcher: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// error channel collecting goroutine errors
|
// error channel collecting goroutine errors
|
||||||
@ -311,18 +321,32 @@ func (r *router) Advertise() (<-chan *Update, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update updates the routing table using the advertised values
|
// Update updates the routing table using the advertised values
|
||||||
func (r *router) Update(a *Update) error {
|
func (r *router) Update(u *Update) error {
|
||||||
// we extract the route from advertisement and update the routing table
|
// NOTE: event sorting might not be necessary
|
||||||
route := Route{
|
// copy update events intp new slices
|
||||||
Destination: a.Event.Route.Destination,
|
events := make([]*Event, len(u.Events))
|
||||||
Gateway: a.Event.Route.Gateway,
|
copy(events, u.Events)
|
||||||
Router: a.Event.Route.Router,
|
// sort events by timestamp
|
||||||
Network: a.Event.Route.Network,
|
sort.Slice(events, func(i, j int) bool {
|
||||||
Metric: a.Event.Route.Metric,
|
return events[i].Timestamp.Before(events[j].Timestamp)
|
||||||
Policy: AddIfNotExists,
|
})
|
||||||
|
|
||||||
|
for _, event := range events {
|
||||||
|
// we extract the route from advertisement and update the routing table
|
||||||
|
route := Route{
|
||||||
|
Destination: event.Route.Destination,
|
||||||
|
Gateway: event.Route.Gateway,
|
||||||
|
Router: event.Route.Router,
|
||||||
|
Network: event.Route.Network,
|
||||||
|
Metric: event.Route.Metric,
|
||||||
|
Policy: AddIfNotExists,
|
||||||
|
}
|
||||||
|
if err := r.opts.Table.Update(route); err != nil {
|
||||||
|
return fmt.Errorf("failed updating routing table: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return r.opts.Table.Update(route)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status returns router status
|
// Status returns router status
|
||||||
|
@ -138,6 +138,7 @@ func (t *table) Update(r Route) error {
|
|||||||
return ErrRouteNotFound
|
return ErrRouteNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if destination has this particular router in the table
|
||||||
if _, ok := t.m[destAddr][sum]; !ok && r.Policy == AddIfNotExists {
|
if _, ok := t.m[destAddr][sum]; !ok && r.Policy == AddIfNotExists {
|
||||||
t.m[destAddr][sum] = r
|
t.m[destAddr][sum] = r
|
||||||
go t.sendEvent(&Event{Type: CreateEvent, Route: r})
|
go t.sendEvent(&Event{Type: CreateEvent, Route: r})
|
||||||
|
@ -34,14 +34,39 @@ type Router interface {
|
|||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Option used by the router
|
||||||
|
type Option func(*Options)
|
||||||
|
|
||||||
|
// UpdateType is route advertisement update type
|
||||||
|
type UpdateType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Announce is advertised when the router announces itself
|
||||||
|
Announce UpdateType = iota
|
||||||
|
// RouteEvent advertises route events
|
||||||
|
RouteEvent
|
||||||
|
)
|
||||||
|
|
||||||
|
// String returns string representation of update event
|
||||||
|
func (ut UpdateType) String() string {
|
||||||
|
switch ut {
|
||||||
|
case Announce:
|
||||||
|
return "ANNOUNCE"
|
||||||
|
case RouteEvent:
|
||||||
|
return "ROUTE"
|
||||||
|
default:
|
||||||
|
return "UNKNOWN"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update is sent by the router to the network
|
// Update is sent by the router to the network
|
||||||
type Update struct {
|
type Update struct {
|
||||||
// ID is the router ID
|
// ID is the router ID
|
||||||
ID string
|
ID string
|
||||||
// Timestamp marks the time when update is sent
|
// Timestamp marks the time when the update is sent
|
||||||
Timestamp time.Time
|
Timestamp time.Time
|
||||||
// Event defines advertisement even
|
// Events is a list of events to advertise
|
||||||
Event *Event
|
Events []*Event
|
||||||
}
|
}
|
||||||
|
|
||||||
// StatusCode defines router status
|
// StatusCode defines router status
|
||||||
@ -58,12 +83,12 @@ type Status struct {
|
|||||||
const (
|
const (
|
||||||
// Init means the rotuer has just been initialized
|
// Init means the rotuer has just been initialized
|
||||||
Init StatusCode = iota
|
Init StatusCode = iota
|
||||||
// Running means the router is running
|
// Running means the router is up and running
|
||||||
Running
|
Running
|
||||||
// Error means the router has crashed with error
|
// Stopped means the router has been stopped
|
||||||
Error
|
|
||||||
// Stopped means the router has stopped
|
|
||||||
Stopped
|
Stopped
|
||||||
|
// Error means the router has encountered error
|
||||||
|
Error
|
||||||
)
|
)
|
||||||
|
|
||||||
// String returns human readable status code
|
// String returns human readable status code
|
||||||
@ -73,18 +98,15 @@ func (sc StatusCode) String() string {
|
|||||||
return "INITIALIZED"
|
return "INITIALIZED"
|
||||||
case Running:
|
case Running:
|
||||||
return "RUNNING"
|
return "RUNNING"
|
||||||
case Error:
|
|
||||||
return "ERROR"
|
|
||||||
case Stopped:
|
case Stopped:
|
||||||
return "STOPPED"
|
return "STOPPED"
|
||||||
|
case Error:
|
||||||
|
return "ERROR"
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN"
|
return "UNKNOWN"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option used by the router
|
|
||||||
type Option func(*Options)
|
|
||||||
|
|
||||||
// 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...)
|
||||||
|
@ -3,6 +3,7 @@ package router
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/olekukonko/tablewriter"
|
"github.com/olekukonko/tablewriter"
|
||||||
)
|
)
|
||||||
@ -16,11 +17,11 @@ var (
|
|||||||
type EventType int
|
type EventType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// CreateEvent is emitted when new route has been created
|
// CreateEvent is emitted when a new route has been created
|
||||||
CreateEvent EventType = iota
|
CreateEvent EventType = iota
|
||||||
// DeleteEvent is emitted when an existing route has been deleted
|
// DeleteEvent is emitted when an existing route has been deleted
|
||||||
DeleteEvent
|
DeleteEvent
|
||||||
// UpdateEvent is emitted when a routing table has been updated
|
// UpdateEvent is emitted when an existing route has been updated
|
||||||
UpdateEvent
|
UpdateEvent
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,6 +43,8 @@ func (et EventType) String() string {
|
|||||||
type Event struct {
|
type Event struct {
|
||||||
// Type defines type of event
|
// Type defines type of event
|
||||||
Type EventType
|
Type EventType
|
||||||
|
// Timestamp is event timestamp
|
||||||
|
Timestamp time.Time
|
||||||
// Route is table rout
|
// Route is table rout
|
||||||
Route Route
|
Route Route
|
||||||
}
|
}
|
||||||
@ -81,18 +84,16 @@ type tableWatcher struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Next returns the next noticed action taken on table
|
// Next returns the next noticed action taken on table
|
||||||
// TODO: this needs to be thought through properly; we only allow watching particular route destination for now
|
// TODO: this needs to be thought through properly;
|
||||||
|
// right now we only allow to watch destination
|
||||||
func (w *tableWatcher) Next() (*Event, error) {
|
func (w *tableWatcher) Next() (*Event, error) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case res := <-w.resChan:
|
case res := <-w.resChan:
|
||||||
switch w.opts.Destination {
|
switch w.opts.Destination {
|
||||||
case "*", "":
|
case res.Route.Destination, "*":
|
||||||
return res, nil
|
return res, nil
|
||||||
default:
|
default:
|
||||||
if w.opts.Destination == res.Route.Destination {
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
case <-w.done:
|
case <-w.done:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user