Added Entry type. Basic implementation of Router and Table
This commit is contained in:
		| @@ -1,6 +1,9 @@ | ||||
| package router | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/google/uuid" | ||||
| 	"github.com/micro/go-micro/registry" | ||||
| 	"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. | ||||
| // It returns error if the entry could not be added. | ||||
| func (r *router) Add(e *Entry, opts ...RouteOption) error { | ||||
| 	return nil | ||||
| func (r *router) Add(e Entry) error { | ||||
| 	return r.table.Add(e) | ||||
| } | ||||
|  | ||||
| // Remove removes entry from the routing table. | ||||
| // It returns error if either the entry could not be removed or it does not exist. | ||||
| func (r *router) Remove(e *Entry) error { | ||||
| 	return nil | ||||
| func (r *router) Remove(e Entry) error { | ||||
| 	return r.table.Remove(e) | ||||
| } | ||||
|  | ||||
| // 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. | ||||
| func (r *router) Update(e *Entry) error { | ||||
| 	return nil | ||||
| func (r *router) Update(opts ...EntryOption) error { | ||||
| 	return r.table.Update(opts...) | ||||
| } | ||||
|  | ||||
| // Lookup makes a query lookup and returns all matching entries | ||||
| func (r *router) Lookup(q Query) ([]*Entry, error) { | ||||
| 	return nil, nil | ||||
| 	return nil, ErrNotImplemented | ||||
| } | ||||
|  | ||||
| // Table returns routing table | ||||
| @@ -83,5 +86,16 @@ func (r *router) Address() string { | ||||
|  | ||||
| // String prints debugging information about router | ||||
| 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 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // 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 | ||||
|  | ||||
| // Policy defines query policy | ||||
| type QueryPolicy int | ||||
| // LookupPolicy defines query policy | ||||
| type LookupPolicy int | ||||
|  | ||||
| const ( | ||||
| 	// DiscardNoRoute discards query when no rout is found | ||||
| 	DiscardNoRoute QueryPolicy = iota | ||||
| 	// ClosestMatch returns closest match to query | ||||
| 	// DiscardNoRoute discards query when no route is found | ||||
| 	DiscardNoRoute LookupPolicy = iota | ||||
| 	// ClosestMatch returns closest match to supplied query | ||||
| 	ClosestMatch | ||||
| ) | ||||
|  | ||||
| // QueryOptions allow to define routing table query options | ||||
| type QueryOptions struct { | ||||
| 	// Route allows to set route options | ||||
| 	Route *RouteOptions | ||||
| 	Route *EntryOptions | ||||
| 	// Service is micro service name | ||||
| 	Service string | ||||
| 	// Policy defines query lookup policy | ||||
| 	Policy QueryPolicy | ||||
| 	Policy LookupPolicy | ||||
| } | ||||
|  | ||||
| // Route allows to set the route query options | ||||
| func Route(r *RouteOptions) QueryOption { | ||||
| // EntryOpts allows to set the route query options | ||||
| func EntryOpts(r *EntryOptions) QueryOption { | ||||
| 	return func(o *QueryOptions) { | ||||
| 		o.Route = r | ||||
| 	} | ||||
| @@ -34,8 +34,8 @@ func Service(s string) QueryOption { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Policy allows to define query lookup policy | ||||
| func Policy(p QueryPolicy) QueryOption { | ||||
| // QueryLookupPolicy allows to define query lookup policy | ||||
| func QueryLookupPolicy(p LookupPolicy) QueryOption { | ||||
| 	return func(o *QueryOptions) { | ||||
| 		o.Policy = p | ||||
| 	} | ||||
|   | ||||
| @@ -8,18 +8,18 @@ type Router interface { | ||||
| 	// Options returns Router options | ||||
| 	Options() Options | ||||
| 	// Add adds new entry into routing table | ||||
| 	Add(*Entry, ...RouteOption) error | ||||
| 	Add(Entry) error | ||||
| 	// Remove removes entry from the routing table | ||||
| 	Remove(*Entry) error | ||||
| 	Remove(Entry) error | ||||
| 	// Update updates entry in the routing table | ||||
| 	Update(*Entry) error | ||||
| 	Update(...EntryOption) error | ||||
| 	// Lookup queries the routing table and returns matching entries | ||||
| 	Lookup(Query) ([]*Entry, error) | ||||
| 	// Table returns routing table | ||||
| 	Table() Table | ||||
| 	// Address is Router adddress | ||||
| 	// Address returns the router bind adddress | ||||
| 	Address() string | ||||
| 	// Network defines network router is in | ||||
| 	// Network returns router's micro network bind address | ||||
| 	Network() string | ||||
| 	// String implemens fmt.Stringer interface | ||||
| 	String() string | ||||
| @@ -31,13 +31,13 @@ type RIB interface { | ||||
| 	String() string | ||||
| } | ||||
|  | ||||
| // Option used by the Router | ||||
| // Option used by the router | ||||
| type Option func(*Options) | ||||
|  | ||||
| // RouteOption is used by Router for adding routing table entries | ||||
| type RouteOption func(*RouteOptions) | ||||
| // EntryOption is used to define routing entry options | ||||
| type EntryOption func(*EntryOptions) | ||||
|  | ||||
| // QueryOption is used to defined routing table lookup query | ||||
| // QueryOption is used to define query options | ||||
| type QueryOption func(*QueryOptions) | ||||
|  | ||||
| // NewRouter creates new Router and returns it | ||||
|   | ||||
							
								
								
									
										133
									
								
								router/table.go
									
									
									
									
									
								
							
							
						
						
									
										133
									
								
								router/table.go
									
									
									
									
									
								
							| @@ -2,7 +2,12 @@ package router | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"hash/fnv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/olekukonko/tablewriter" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| @@ -10,77 +15,151 @@ var ( | ||||
| 	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(*Entry) error | ||||
| 	Add(Entry) error | ||||
| 	// Remove removes route from the table | ||||
| 	Remove(*Entry) error | ||||
| 	Remove(Entry) error | ||||
| 	// Update updates route in the table | ||||
| 	Update(*Entry) error | ||||
| 	Update(...EntryOption) error | ||||
| 	// Lookup looks up routes in the table | ||||
| 	Lookup(Query) ([]*Entry, error) | ||||
| 	Lookup(Query) ([]Entry, error) | ||||
| 	// Size returns the size of the table | ||||
| 	Size() int | ||||
| 	// String prints the routing table | ||||
| 	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 | ||||
| // It maps service name to routes | ||||
| type table struct { | ||||
| 	// m stores routing table map | ||||
| 	m map[string][]Entry | ||||
| 	m map[string]map[uint64]Entry | ||||
| 	sync.RWMutex | ||||
| } | ||||
|  | ||||
| // NewTable creates new routing table and returns it | ||||
| func NewTable() Table { | ||||
| 	return &table{ | ||||
| 		m: make(map[string][]Entry), | ||||
| 		m: make(map[string]map[uint64]Entry), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Add adds new routing entry | ||||
| func (t *table) Add(e *Entry) error { | ||||
| 	return nil | ||||
| 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 | ||||
| 	} | ||||
|  | ||||
| 	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 | ||||
| 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 | ||||
| // 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 | ||||
| func (t *table) Lookup(q Query) ([]*Entry, error) { | ||||
| 	return nil, nil | ||||
| func (t *table) Lookup(q Query) ([]Entry, 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 { | ||||
| 	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