Added Entry type. Basic implementation of Router and Table

This commit is contained in:
Milos Gajdos 2019-06-09 23:09:38 +01:00
parent ad92e6821e
commit 9c57f32f58
No known key found for this signature in database
GPG Key ID: 8B31058CC55DFD4F
6 changed files with 237 additions and 79 deletions

View File

@ -1,6 +1,9 @@
package router package router
import ( import (
"fmt"
"strings"
"github.com/google/uuid" "github.com/google/uuid"
"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"
@ -45,25 +48,25 @@ func (r *router) Options() Options {
// Add adds new entry into routing table with given options. // Add adds new entry into routing table with given options.
// It returns error if the entry could not be added. // It returns error if the entry could not be added.
func (r *router) Add(e *Entry, opts ...RouteOption) error { func (r *router) Add(e Entry) error {
return nil return r.table.Add(e)
} }
// Remove removes entry from the routing table. // Remove removes entry from the routing table.
// It returns error if either the entry could not be removed or it does not exist. // It returns error if either the entry could not be removed or it does not exist.
func (r *router) Remove(e *Entry) error { func (r *router) Remove(e Entry) error {
return nil return r.table.Remove(e)
} }
// Update updates an entry in the router's routing table // Update updates an entry in the router's routing table
// It returns error if the entry was not found or it failed to be updated. // It returns error if the entry was not found or it failed to be updated.
func (r *router) Update(e *Entry) error { func (r *router) Update(opts ...EntryOption) error {
return nil return r.table.Update(opts...)
} }
// Lookup makes a query lookup and returns all matching entries // Lookup makes a query lookup and returns all matching entries
func (r *router) Lookup(q Query) ([]*Entry, error) { func (r *router) Lookup(q Query) ([]*Entry, error) {
return nil, nil return nil, ErrNotImplemented
} }
// Table returns routing table // Table returns routing table
@ -83,5 +86,16 @@ func (r *router) Address() string {
// String prints debugging information about router // String prints debugging information about router
func (r *router) String() string { func (r *router) String() string {
return "" sb := &strings.Builder{}
s := fmt.Sprintf("Router Local Address: %s\n", r.opts.Address)
sb.WriteString(s)
s = fmt.Sprintf("Router Network Address: %s\n", r.opts.Network)
sb.WriteString(s)
s = fmt.Sprintf("Routing table size: %d\n", r.opts.Table.Size())
sb.WriteString(s)
return sb.String()
} }

89
router/entry.go Normal file
View File

@ -0,0 +1,89 @@
package router
// AddPolicy defines routing table addition policy
type AddPolicy int
const (
// Override overrides existing routing table entry
OverrideIfExists AddPolicy = iota
// ErrIfExists returns error if the entry already exists
ErrIfExists
)
// EntryOptions defines micro network routing table entry options
type EntryOptions struct {
// DestAddr is destination address
DestAddr string
// Hop is the next route hop
Hop Router
// SrcAddr defines local routing address
// On local networkss, this will be the address of local router
SrcAddr string
// Metric is route cost metric
Metric int
// Policy defines entry addition policy
Policy AddPolicy
}
// DestAddr sets destination address
func DestAddr(a string) EntryOption {
return func(o *EntryOptions) {
o.DestAddr = a
}
}
// Hop allows to set the route entry options
func Hop(r Router) EntryOption {
return func(o *EntryOptions) {
o.Hop = r
}
}
// SrcAddr sets source address
func SrcAddr(a string) EntryOption {
return func(o *EntryOptions) {
o.SrcAddr = a
}
}
// Metric sets entry metric
func Metric(m int) EntryOption {
return func(o *EntryOptions) {
o.Metric = m
}
}
// AddEntryPolicy sets add entry policy
func AddEntryPolicy(p AddPolicy) EntryOption {
return func(o *EntryOptions) {
o.Policy = p
}
}
// Entry is routing table entry
type Entry interface {
// Options returns entry options
Options() EntryOptions
}
type entry struct {
opts EntryOptions
}
// NewEntry returns new routing table entry
func NewEntry(opts ...EntryOption) Entry {
eopts := EntryOptions{}
for _, o := range opts {
o(&eopts)
}
return &entry{
opts: eopts,
}
}
// Options returns entry options
func (e *entry) Options() EntryOptions {
return e.opts
}

View File

@ -45,27 +45,3 @@ func RoutingTable(t Table) Option {
o.Table = t o.Table = t
} }
} }
// RouteOptions allows to specify routing table options
type RouteOptions struct {
// NetID is network ID
NetID string
// Metric is route metric
Metric int
// COntext allows to specify other arbitrary options
Context context.Context
}
// NetID allows to set micro network ID
func NetID(id string) RouteOption {
return func(o *RouteOptions) {
o.NetID = id
}
}
// Metric allows to set route cost metric
func Metric(m int) RouteOption {
return func(o *RouteOptions) {
o.Metric = m
}
}

View File

@ -1,27 +1,27 @@
package router package router
// Policy defines query policy // LookupPolicy defines query policy
type QueryPolicy int type LookupPolicy int
const ( const (
// DiscardNoRoute discards query when no rout is found // DiscardNoRoute discards query when no route is found
DiscardNoRoute QueryPolicy = iota DiscardNoRoute LookupPolicy = iota
// ClosestMatch returns closest match to query // ClosestMatch returns closest match to supplied query
ClosestMatch ClosestMatch
) )
// 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 Route *EntryOptions
// Service is micro service name // Service is micro service name
Service string Service string
// Policy defines query lookup policy // Policy defines query lookup policy
Policy QueryPolicy Policy LookupPolicy
} }
// Route allows to set the route query options // EntryOpts allows to set the route query options
func Route(r *RouteOptions) QueryOption { func EntryOpts(r *EntryOptions) QueryOption {
return func(o *QueryOptions) { return func(o *QueryOptions) {
o.Route = r o.Route = r
} }
@ -34,8 +34,8 @@ func Service(s string) QueryOption {
} }
} }
// Policy allows to define query lookup policy // QueryLookupPolicy allows to define query lookup policy
func Policy(p QueryPolicy) QueryOption { func QueryLookupPolicy(p LookupPolicy) QueryOption {
return func(o *QueryOptions) { return func(o *QueryOptions) {
o.Policy = p o.Policy = p
} }

View File

@ -8,18 +8,18 @@ type Router interface {
// Options returns Router options // Options returns Router options
Options() Options Options() Options
// Add adds new entry into routing table // Add adds new entry into routing table
Add(*Entry, ...RouteOption) error Add(Entry) error
// Remove removes entry from the routing table // Remove removes entry from the routing table
Remove(*Entry) error Remove(Entry) error
// Update updates entry in the routing table // Update updates entry in the routing table
Update(*Entry) error Update(...EntryOption) error
// Lookup queries the routing table and returns matching entries // Lookup queries the routing table and returns matching entries
Lookup(Query) ([]*Entry, error) Lookup(Query) ([]*Entry, error)
// Table returns routing table // Table returns routing table
Table() Table Table() Table
// Address is Router adddress // Address returns the router bind adddress
Address() string Address() string
// Network defines network router is in // Network returns router's micro network bind address
Network() string Network() string
// String implemens fmt.Stringer interface // String implemens fmt.Stringer interface
String() string String() string
@ -31,13 +31,13 @@ type RIB interface {
String() string String() string
} }
// Option used by the Router // Option used by the router
type Option func(*Options) type Option func(*Options)
// RouteOption is used by Router for adding routing table entries // EntryOption is used to define routing entry options
type RouteOption func(*RouteOptions) type EntryOption func(*EntryOptions)
// QueryOption is used to defined routing table lookup query // QueryOption is used to define query options
type QueryOption func(*QueryOptions) type QueryOption func(*QueryOptions)
// NewRouter creates new Router and returns it // NewRouter creates new Router and returns it

View File

@ -2,7 +2,12 @@ package router
import ( import (
"errors" "errors"
"fmt"
"hash/fnv"
"strings"
"sync" "sync"
"github.com/olekukonko/tablewriter"
) )
var ( var (
@ -10,77 +15,151 @@ var (
ErrRouteNotFound = errors.New("route not found") ErrRouteNotFound = errors.New("route not found")
// ErrDuplicateRoute is return when route already exists // ErrDuplicateRoute is return when route already exists
ErrDuplicateRoute = errors.New("duplicate route") ErrDuplicateRoute = errors.New("duplicate route")
// ErrNotImplemented is returned when some functionality has not been implemented
ErrNotImplemented = errors.New("not implemented")
) )
// Table is routing table // Table is routing table
type Table interface { type Table interface {
// Add adds new route to the table // Add adds new route to the table
Add(*Entry) error Add(Entry) error
// Remove removes route from the table // Remove removes route from the table
Remove(*Entry) error Remove(Entry) error
// Update updates route in the table // Update updates route in the table
Update(*Entry) error Update(...EntryOption) error
// Lookup looks up routes in the table // Lookup looks up routes in the table
Lookup(Query) ([]*Entry, error) Lookup(Query) ([]Entry, 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
String() string String() string
} }
// Entry is micro network routing table entry
type Entry struct {
// Addr is destination address
Addr string
// NetID is micro network ID
NetID string
// Hop is the next route hop
Hop Router
// Metric is route cost metric
Metric int
}
// table is routing table // table is routing table
// It maps service name to routes // It maps service name to routes
type table struct { type table struct {
// m stores routing table map // m stores routing table map
m map[string][]Entry m map[string]map[uint64]Entry
sync.RWMutex sync.RWMutex
} }
// NewTable creates new routing table and returns it // NewTable creates new routing table and returns it
func NewTable() Table { func NewTable() Table {
return &table{ return &table{
m: make(map[string][]Entry), m: make(map[string]map[uint64]Entry),
} }
} }
// Add adds new routing entry // Add adds new routing entry
func (t *table) Add(e *Entry) error { func (t *table) Add(e Entry) error {
t.Lock()
defer t.Unlock()
destAddr := e.Options().DestAddr
h := fnv.New64()
h.Write([]byte(e.Options().DestAddr + e.Options().Hop.Address()))
if _, ok := t.m[destAddr]; !ok {
// create new map for DestAddr routes
t.m[destAddr] = make(map[uint64]Entry)
t.m[destAddr][h.Sum64()] = e
return nil return nil
}
if _, ok := t.m[destAddr][h.Sum64()]; ok && e.Options().Policy == OverrideIfExists {
t.m[destAddr][h.Sum64()] = e
return nil
}
return ErrDuplicateRoute
} }
// Remove removes entry from the routing table // Remove removes entry from the routing table
func (t *table) Remove(e *Entry) error { func (t *table) Remove(e Entry) error {
t.Lock()
defer t.Unlock()
destAddr := e.Options().DestAddr
h := fnv.New64()
h.Write([]byte(e.Options().DestAddr + e.Options().Hop.Address()))
if _, ok := t.m[destAddr]; !ok {
return ErrRouteNotFound
} else {
delete(t.m[destAddr], h.Sum64())
return nil
}
return nil return nil
} }
// Update updates routin entry // Update updates routing entry
func (t *table) Update(e *Entry) error { func (t *table) Update(opts ...EntryOption) error {
t.Lock()
defer t.Unlock()
e := NewEntry(opts...)
destAddr := e.Options().DestAddr
h := fnv.New64()
h.Write([]byte(e.Options().DestAddr + e.Options().Hop.Address()))
if _, ok := t.m[destAddr]; !ok {
return ErrRouteNotFound
}
if _, ok := t.m[destAddr][h.Sum64()]; ok {
t.m[destAddr][h.Sum64()] = e
return nil return nil
}
return ErrRouteNotFound
} }
// Lookup looks up entry in the routing table // Lookup looks up entry in the routing table
func (t *table) Lookup(q Query) ([]*Entry, error) { func (t *table) Lookup(q Query) ([]Entry, error) {
return nil, nil return nil, ErrNotImplemented
} }
// 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()
defer t.RUnlock()
return len(t.m) return len(t.m)
} }
// String returns text representation of routing table // String returns text representation of routing table
func (t *table) String() string { func (t *table) String() string {
return "" 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{"Dest", "Hop", "Src", "Metric"})
var destAddr, prevAddr string
for _, entries := range t.m {
for _, entry := range entries {
destAddr = entry.Options().DestAddr
// we want to avoid printing the same dest address
if prevAddr == destAddr {
destAddr = ""
}
strEntry := []string{
destAddr,
entry.Options().Hop.Address(),
fmt.Sprintf("%d", entry.Options().SrcAddr),
fmt.Sprintf("%d", entry.Options().Metric),
}
table.Append(strEntry)
prevAddr = destAddr
}
}
return sb.String()
} }