micro/router/table.go

168 lines
3.1 KiB
Go
Raw Normal View History

package router
import (
"errors"
"fmt"
"hash"
"hash/fnv"
"strings"
"sync"
"github.com/olekukonko/tablewriter"
)
var (
// ErrRouteNotFound is returned when no route was found
ErrRouteNotFound = errors.New("route not found")
// ErrDuplicateRoute is return when route already exists
ErrDuplicateRoute = errors.New("duplicate route")
// ErrNotImplemented is returned when some functionality has not been implemented
ErrNotImplemented = errors.New("not implemented")
)
// Table is routing table
type Table interface {
// Add adds new route to the table
Add(Route) error
// Remove removes route from the table
Remove(Route) error
// Update updates route in the table
Update(...RouteOption) error
// Lookup looks up routes in the table
Lookup(Query) ([]Route, error)
// Size returns the size of the table
Size() int
// String prints the routing table
String() string
}
// table is routing table
// It maps service name to routes
type table struct {
// m stores routing table map
m map[uint64]Route
// h is a hasher hashes route entries
h hash.Hash64
sync.RWMutex
}
// NewTable creates new routing table and returns it
func NewTable() Table {
h := fnv.New64()
h.Reset()
return &table{
m: make(map[uint64]Route),
h: h,
}
}
// Add adds new routing entry
func (t *table) Add(r Route) error {
t.Lock()
defer t.Unlock()
sum := t.hash(r)
if _, ok := t.m[sum]; !ok {
t.m[sum] = r
return nil
}
if _, ok := t.m[sum]; ok && r.Options().Policy == OverrideIfExists {
t.m[sum] = r
return nil
}
return ErrDuplicateRoute
}
// Remove removes entry from the routing table
func (t *table) Remove(r Route) error {
t.Lock()
defer t.Unlock()
sum := t.hash(r)
if _, ok := t.m[sum]; !ok {
return ErrRouteNotFound
}
delete(t.m, sum)
return nil
}
// Update updates routing entry
func (t *table) Update(opts ...RouteOption) error {
t.Lock()
defer t.Unlock()
r := NewRoute(opts...)
sum := t.hash(r)
if _, ok := t.m[sum]; !ok {
return ErrRouteNotFound
}
if _, ok := t.m[sum]; ok {
t.m[sum] = r
return nil
}
return ErrRouteNotFound
}
// Lookup looks up entry in the routing table
func (t *table) Lookup(q Query) ([]Route, error) {
return nil, ErrNotImplemented
}
// Size returns the size of the routing table
func (t *table) Size() int {
t.RLock()
defer t.RUnlock()
return len(t.m)
}
// String returns text representation of routing table
func (t *table) String() string {
t.RLock()
defer t.RUnlock()
// this will help us build routing table string
sb := &strings.Builder{}
// create nice table printing structure
table := tablewriter.NewWriter(sb)
table.SetHeader([]string{"Service", "Gateway", "Network", "Metric"})
for _, route := range t.m {
strRoute := []string{
route.Options().DestAddr,
route.Options().Hop.Address(),
route.Options().Network,
fmt.Sprintf("%d", route.Options().Metric),
}
table.Append(strRoute)
}
// render table into sb
table.Render()
return sb.String()
}
func (t *table) hash(r Route) uint64 {
destAddr := r.Options().DestAddr
routerAddr := r.Options().Hop.Address()
network := r.Options().Network
t.h.Reset()
t.h.Write([]byte(destAddr + routerAddr + network))
return t.h.Sum64()
}