Added Entry type. Basic implementation of Router and Table
This commit is contained in:
		| @@ -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
									
								
							
							
						
						
									
										89
									
								
								router/entry.go
									
									
									
									
									
										Normal 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 | ||||||
|  | } | ||||||
| @@ -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 |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -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 | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
							
								
								
									
										129
									
								
								router/table.go
									
									
									
									
									
								
							
							
						
						
									
										129
									
								
								router/table.go
									
									
									
									
									
								
							| @@ -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 | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| // Update updates routin entry |  | ||||||
| func (t *table) Update(e *Entry) error { |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Update updates routing entry | ||||||
|  | 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 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() | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user