Route has changed to accomodate Link, Service and Address

This commit is contained in:
Milos Gajdos 2019-07-09 15:45:42 +01:00
parent 449aa0a339
commit 70665e5a7d
No known key found for this signature in database
GPG Key ID: 8B31058CC55DFD4F
7 changed files with 132 additions and 131 deletions

View File

@ -105,10 +105,11 @@ func (r *router) manageServiceRoutes(service *registry.Service, action string) e
// take route action on each service node // take route action on each service node
for _, node := range service.Nodes { for _, node := range service.Nodes {
route := table.Route{ route := table.Route{
Destination: service.Name, Service: service.Name,
Gateway: node.Address, Address: node.Address,
Router: r.opts.Address, Gateway: "",
Network: r.opts.Network, Network: r.opts.Network,
Link: table.DefaultLink,
Metric: table.DefaultLocalMetric, Metric: table.DefaultLocalMetric,
} }
switch action { switch action {
@ -439,9 +440,9 @@ func (r *router) Advertise() (<-chan *Advert, error) {
if r.opts.Gateway != "" { if r.opts.Gateway != "" {
// note, the only non-default value is the gateway // note, the only non-default value is the gateway
route := table.Route{ route := table.Route{
Destination: "*", Service: "*",
Address: "*",
Gateway: r.opts.Gateway, Gateway: r.opts.Gateway,
Router: "*",
Network: "*", Network: "*",
Metric: table.DefaultLocalMetric, Metric: table.DefaultLocalMetric,
} }
@ -530,14 +531,8 @@ func (r *router) Update(a *Advert) error {
}) })
for _, event := range events { for _, event := range events {
// we extract the route from advertisement and update the routing table // create a copy of the route
route := table.Route{ route := event.Route
Destination: event.Route.Destination,
Gateway: event.Route.Gateway,
Router: event.Route.Router,
Network: event.Route.Network,
Metric: event.Route.Metric,
}
if err := r.opts.Table.Update(route); err != nil { if err := r.opts.Table.Update(route); err != nil {
return fmt.Errorf("failed updating routing table: %v", err) return fmt.Errorf("failed updating routing table: %v", err)
} }

View File

@ -10,7 +10,7 @@ var (
// DefaultAddress is default router address // DefaultAddress is default router address
DefaultAddress = ":9093" DefaultAddress = ":9093"
// DefaultNetwork is default micro network // DefaultNetwork is default micro network
DefaultNetwork = "micro.mu" DefaultNetwork = "go.micro"
) )
// Options are router options // Options are router options
@ -19,10 +19,10 @@ type Options struct {
ID string ID string
// Address is router address // Address is router address
Address string Address string
// Network is micro network
Network string
// Gateway is micro network gateway // Gateway is micro network gateway
Gateway string Gateway string
// Network is micro network
Network string
// Registry is the local registry // Registry is the local registry
Registry registry.Registry Registry registry.Registry
// Table is routing table // Table is routing table
@ -43,13 +43,6 @@ func Address(a string) Option {
} }
} }
// Network sets router network
func Network(n string) Option {
return func(o *Options) {
o.Network = n
}
}
// Gateway sets network gateway // Gateway sets network gateway
func Gateway(g string) Option { func Gateway(g string) Option {
return func(o *Options) { return func(o *Options) {
@ -57,6 +50,13 @@ func Gateway(g string) Option {
} }
} }
// Network sets router network
func Network(n string) Option {
return func(o *Options) {
o.Network = n
}
}
// RoutingTable sets the routing table // RoutingTable sets the routing table
func RoutingTable(t table.Table) Option { func RoutingTable(t table.Table) Option {
return func(o *Options) { return func(o *Options) {

View File

@ -57,23 +57,23 @@ func (t *table) Options() TableOptions {
// Add adds a route to the routing table // Add adds a route to the routing table
func (t *table) Add(r Route) error { func (t *table) Add(r Route) error {
destAddr := r.Destination service := r.Service
sum := r.Hash() sum := r.Hash()
t.Lock() t.Lock()
defer t.Unlock() defer t.Unlock()
// check if there are any routes in the table for the route destination // check if there are any routes in the table for the route destination
if _, ok := t.m[destAddr]; !ok { if _, ok := t.m[service]; !ok {
t.m[destAddr] = make(map[uint64]Route) t.m[service] = make(map[uint64]Route)
t.m[destAddr][sum] = r t.m[service][sum] = r
go t.sendEvent(&Event{Type: Insert, Route: r}) go t.sendEvent(&Event{Type: Insert, Route: r})
return nil return nil
} }
// add new route to the table for the route destination // add new route to the table for the route destination
if _, ok := t.m[destAddr][sum]; !ok { if _, ok := t.m[service][sum]; !ok {
t.m[destAddr][sum] = r t.m[service][sum] = r
go t.sendEvent(&Event{Type: Insert, Route: r}) go t.sendEvent(&Event{Type: Insert, Route: r})
return nil return nil
} }
@ -83,17 +83,17 @@ func (t *table) Add(r Route) error {
// Delete deletes the route from the routing table // Delete deletes the route from the routing table
func (t *table) Delete(r Route) error { func (t *table) Delete(r Route) error {
destAddr := r.Destination service := r.Service
sum := r.Hash() sum := r.Hash()
t.Lock() t.Lock()
defer t.Unlock() defer t.Unlock()
if _, ok := t.m[destAddr]; !ok { if _, ok := t.m[service]; !ok {
return ErrRouteNotFound return ErrRouteNotFound
} }
delete(t.m[destAddr], sum) delete(t.m[service], sum)
go t.sendEvent(&Event{Type: Delete, Route: r}) go t.sendEvent(&Event{Type: Delete, Route: r})
return nil return nil
@ -101,20 +101,20 @@ func (t *table) Delete(r Route) error {
// Update updates routing table with the new route // Update updates routing table with the new route
func (t *table) Update(r Route) error { func (t *table) Update(r Route) error {
destAddr := r.Destination service := r.Service
sum := r.Hash() sum := r.Hash()
t.Lock() t.Lock()
defer t.Unlock() defer t.Unlock()
// check if the route destination has any routes in the table // check if the route destination has any routes in the table
if _, ok := t.m[destAddr]; !ok { if _, ok := t.m[service]; !ok {
return ErrRouteNotFound return ErrRouteNotFound
} }
// if the route has been found update it // if the route has been found update it
if _, ok := t.m[destAddr][sum]; ok { if _, ok := t.m[service][sum]; ok {
t.m[destAddr][sum] = r t.m[service][sum] = r
go t.sendEvent(&Event{Type: Update, Route: r}) go t.sendEvent(&Event{Type: Update, Route: r})
return nil return nil
} }
@ -140,7 +140,7 @@ func (t *table) List() ([]Route, error) {
// isMatch checks if the route matches given network and router // isMatch checks if the route matches given network and router
func isMatch(route Route, network, router string) bool { func isMatch(route Route, network, router string) bool {
if network == "*" || network == route.Network { if network == "*" || network == route.Network {
if router == "*" || router == route.Router { if router == "*" || router == route.Gateway {
return true return true
} }
} }
@ -163,18 +163,18 @@ func (t *table) Lookup(q Query) ([]Route, error) {
t.RLock() t.RLock()
defer t.RUnlock() defer t.RUnlock()
if q.Options().Destination != "*" { if q.Options().Service != "*" {
// no routes found for the destination and query policy is not a DiscardIfNone // no routes found for the destination and query policy is not a DiscardIfNone
if _, ok := t.m[q.Options().Destination]; !ok && q.Options().Policy != DiscardIfNone { if _, ok := t.m[q.Options().Service]; !ok && q.Options().Policy != DiscardIfNone {
return nil, ErrRouteNotFound return nil, ErrRouteNotFound
} }
return findRoutes(t.m[q.Options().Destination], q.Options().Network, q.Options().Router), nil return findRoutes(t.m[q.Options().Service], q.Options().Network, q.Options().Gateway), nil
} }
var results []Route var results []Route
// search through all destinations // search through all destinations
for _, routes := range t.m { for _, routes := range t.m {
results = append(results, findRoutes(routes, q.Options().Network, q.Options().Router)...) results = append(results, findRoutes(routes, q.Options().Network, q.Options().Gateway)...)
} }
return results, nil return results, nil
@ -184,7 +184,7 @@ func (t *table) Lookup(q Query) ([]Route, error) {
func (t *table) Watch(opts ...WatchOption) (Watcher, error) { func (t *table) Watch(opts ...WatchOption) (Watcher, error) {
// by default watch everything // by default watch everything
wopts := WatchOptions{ wopts := WatchOptions{
Destination: "*", Service: "*",
} }
for _, o := range opts { for _, o := range opts {
@ -244,15 +244,16 @@ func (t *table) String() string {
// create nice table printing structure // create nice table printing structure
table := tablewriter.NewWriter(sb) table := tablewriter.NewWriter(sb)
table.SetHeader([]string{"Destination", "Gateway", "Router", "Network", "Metric"}) table.SetHeader([]string{"Service", "Address", "Gateway", "Network", "Link", "Metric"})
for _, destRoute := range t.m { for _, destRoute := range t.m {
for _, route := range destRoute { for _, route := range destRoute {
strRoute := []string{ strRoute := []string{
route.Destination, route.Service,
route.Address,
route.Gateway, route.Gateway,
route.Router,
route.Network, route.Network,
route.Link,
fmt.Sprintf("%d", route.Metric), fmt.Sprintf("%d", route.Metric),
} }
table.Append(strRoute) table.Append(strRoute)

View File

@ -6,10 +6,10 @@ func testSetup() (Table, Route) {
table := NewTable() table := NewTable()
route := Route{ route := Route{
Destination: "dest.svc", Service: "dest.svc",
Gateway: "dest.gw", Gateway: "dest.gw",
Router: "dest.router",
Network: "dest.network", Network: "dest.network",
Link: "det.link",
Metric: 10, Metric: 10,
} }
@ -34,7 +34,7 @@ func TestAdd(t *testing.T) {
testTableSize += 1 testTableSize += 1
if table.Size() != testTableSize { if table.Size() != testTableSize {
t.Errorf("invalid number of routes. expected: %d, found: %d", testTableSize, table.Size()) t.Errorf("invalid number of routes. Expected: %d, found: %d", testTableSize, table.Size())
} }
// adding the same route under Insert policy must error // adding the same route under Insert policy must error
@ -53,15 +53,15 @@ func TestDelete(t *testing.T) {
testTableSize += 1 testTableSize += 1
// should fail to delete non-existant route // should fail to delete non-existant route
prevDest := route.Destination prevSvc := route.Service
route.Destination = "randDest" route.Service = "randDest"
if err := table.Delete(route); err != ErrRouteNotFound { if err := table.Delete(route); err != ErrRouteNotFound {
t.Errorf("error deleting route. Expected error: %s, found: %s", ErrRouteNotFound, err) t.Errorf("error deleting route. Expected: %s, found: %s", ErrRouteNotFound, err)
} }
// we should be able to delete the existing route // we should be able to delete the existing route
route.Destination = prevDest route.Service = prevSvc
if err := table.Delete(route); err != nil { if err := table.Delete(route); err != nil {
t.Errorf("error deleting route: %s", err) t.Errorf("error deleting route: %s", err)
@ -69,7 +69,7 @@ func TestDelete(t *testing.T) {
testTableSize -= 1 testTableSize -= 1
if table.Size() != testTableSize { if table.Size() != testTableSize {
t.Errorf("invalid number of routes. expected: %d, found: %d", testTableSize, table.Size()) t.Errorf("invalid number of routes. Expected: %d, found: %d", testTableSize, table.Size())
} }
} }
@ -91,28 +91,28 @@ func TestUpdate(t *testing.T) {
// the size of the table should not change as we're only updating the metric of an existing route // the size of the table should not change as we're only updating the metric of an existing route
if table.Size() != testTableSize { if table.Size() != testTableSize {
t.Errorf("invalid number of routes. expected: %d, found: %d", testTableSize, table.Size()) t.Errorf("invalid number of routes. Expected: %d, found: %d", testTableSize, table.Size())
} }
// this should error as the destination does not exist // this should error as the destination does not exist
route.Destination = "rand.dest" route.Service = "rand.dest"
if err := table.Update(route); err != ErrRouteNotFound { if err := table.Update(route); err != ErrRouteNotFound {
t.Errorf("error updating route. Expected error: %s, found: %s", ErrRouteNotFound, err) t.Errorf("error updating route. Expected error: %s, found: %s", ErrRouteNotFound, err)
} }
if table.Size() != testTableSize { if table.Size() != testTableSize {
t.Errorf("invalid number of routes. expected: %d, found: %d", testTableSize, table.Size()) t.Errorf("invalid number of routes. Expected: %d, found: %d", testTableSize, table.Size())
} }
} }
func TestList(t *testing.T) { func TestList(t *testing.T) {
table, route := testSetup() table, route := testSetup()
dest := []string{"one.svc", "two.svc", "three.svc"} svc := []string{"one.svc", "two.svc", "three.svc"}
for i := 0; i < len(dest); i++ { for i := 0; i < len(svc); i++ {
route.Destination = dest[i] route.Service = svc[i]
if err := table.Add(route); err != nil { if err := table.Add(route); err != nil {
t.Errorf("error adding route: %s", err) t.Errorf("error adding route: %s", err)
} }
@ -123,26 +123,26 @@ func TestList(t *testing.T) {
t.Errorf("error listing routes: %s", err) t.Errorf("error listing routes: %s", err)
} }
if len(routes) != len(dest) { if len(routes) != len(svc) {
t.Errorf("incorrect number of routes listed. Expected: %d, found: %d", len(dest), len(routes)) t.Errorf("incorrect number of routes listed. Expected: %d, found: %d", len(svc), len(routes))
} }
if len(routes) != table.Size() { if len(routes) != table.Size() {
t.Errorf("mismatch number of routes and table size. Routes: %d, Size: %d", len(routes), table.Size()) t.Errorf("mismatch number of routes and table size. Expected: %d, found: %d", len(routes), table.Size())
} }
} }
func TestLookup(t *testing.T) { func TestLookup(t *testing.T) {
table, route := testSetup() table, route := testSetup()
dest := []string{"svc1", "svc2", "svc3"} svc := []string{"svc1", "svc2", "svc3"}
net := []string{"net1", "net2", "net1"} net := []string{"net1", "net2", "net1"}
rtr := []string{"router1", "router2", "router3"} gw := []string{"gw1", "gw2", "gw3"}
for i := 0; i < len(dest); i++ { for i := 0; i < len(svc); i++ {
route.Destination = dest[i] route.Service = svc[i]
route.Network = net[i] route.Network = net[i]
route.Router = rtr[i] route.Gateway = gw[i]
if err := table.Add(route); err != nil { if err := table.Add(route); err != nil {
t.Errorf("error adding route: %s", err) t.Errorf("error adding route: %s", err)
} }
@ -157,7 +157,7 @@ func TestLookup(t *testing.T) {
} }
if len(routes) != table.Size() { if len(routes) != table.Size() {
t.Errorf("incorrect number of routes returned. expected: %d, found: %d", table.Size(), len(routes)) t.Errorf("incorrect number of routes returned. Expected: %d, found: %d", table.Size(), len(routes))
} }
// query particular net // query particular net
@ -169,12 +169,12 @@ func TestLookup(t *testing.T) {
} }
if len(routes) != 2 { if len(routes) != 2 {
t.Errorf("incorrect number of routes returned. expected: %d, found: %d", 2, len(routes)) t.Errorf("incorrect number of routes returned. Expected: %d, found: %d", 2, len(routes))
} }
// query particular router // query particular gateway
router := "router1" gateway := "gw1"
query = NewQuery(QueryRouter(router)) query = NewQuery(QueryGateway(gateway))
routes, err = table.Lookup(query) routes, err = table.Lookup(query)
if err != nil { if err != nil {
@ -182,17 +182,17 @@ func TestLookup(t *testing.T) {
} }
if len(routes) != 1 { if len(routes) != 1 {
t.Errorf("incorrect number of routes returned. expected: %d, found: %d", 1, len(routes)) t.Errorf("incorrect number of routes returned. Expected: %d, found: %d", 1, len(routes))
} }
if routes[0].Router != router { if routes[0].Gateway != gateway {
t.Errorf("incorrect route returned. Expected router: %s, found: %s", router, routes[0].Router) t.Errorf("incorrect route returned. Expected gateway: %s, found: %s", gateway, routes[0].Gateway)
} }
// query particular route // query particular route
network := "net1" network := "net1"
query = NewQuery( query = NewQuery(
QueryRouter(router), QueryGateway(gateway),
QueryNetwork(network), QueryNetwork(network),
) )
@ -202,11 +202,11 @@ func TestLookup(t *testing.T) {
} }
if len(routes) != 1 { if len(routes) != 1 {
t.Errorf("incorrect number of routes returned. expected: %d, found: %d", 1, len(routes)) t.Errorf("incorrect number of routes returned. Expected: %d, found: %d", 1, len(routes))
} }
if routes[0].Router != router { if routes[0].Gateway != gateway {
t.Errorf("incorrect route returned. Expected router: %s, found: %s", router, routes[0].Router) t.Errorf("incorrect route returned. Expected gateway: %s, found: %s", gateway, routes[0].Gateway)
} }
if routes[0].Network != network { if routes[0].Network != network {
@ -214,7 +214,7 @@ func TestLookup(t *testing.T) {
} }
// bullshit route query // bullshit route query
query = NewQuery(QueryDestination("foobar")) query = NewQuery(QueryService("foobar"))
routes, err = table.Lookup(query) routes, err = table.Lookup(query)
if err != nil { if err != nil {
@ -222,6 +222,6 @@ func TestLookup(t *testing.T) {
} }
if len(routes) != 0 { if len(routes) != 0 {
t.Errorf("incorrect number of routes returned. expected: %d, found: %d", 0, len(routes)) t.Errorf("incorrect number of routes returned. Expected: %d, found: %d", 0, len(routes))
} }
} }

View File

@ -34,34 +34,34 @@ type QueryOption func(*QueryOptions)
// QueryOptions are routing table query options // QueryOptions are routing table query options
type QueryOptions struct { type QueryOptions struct {
// Destination is destination address // Service is destination service name
Destination string Service string
// Gateway is route gateway
Gateway string
// Network is network address // Network is network address
Network string Network string
// Router is router address
Router string
// Policy is query lookup policy // Policy is query lookup policy
Policy LookupPolicy Policy LookupPolicy
} }
// QueryDestination sets destination address // QueryService sets destination address
func QueryDestination(d string) QueryOption { func QueryService(s string) QueryOption {
return func(o *QueryOptions) { return func(o *QueryOptions) {
o.Destination = d o.Service = s
}
}
// QueryGateway sets route gateway
func QueryGateway(g string) QueryOption {
return func(o *QueryOptions) {
o.Gateway = g
} }
} }
// QueryNetwork sets route network address // QueryNetwork sets route network address
func QueryNetwork(a string) QueryOption { func QueryNetwork(n string) QueryOption {
return func(o *QueryOptions) { return func(o *QueryOptions) {
o.Network = a o.Network = n
}
}
// QueryRouter sets route router address
func QueryRouter(r string) QueryOption {
return func(o *QueryOptions) {
o.Router = r
} }
} }
@ -89,8 +89,8 @@ func NewQuery(opts ...QueryOption) Query {
// default options // default options
// NOTE: by default we use DefaultNetworkMetric // NOTE: by default we use DefaultNetworkMetric
qopts := QueryOptions{ qopts := QueryOptions{
Destination: "*", Service: "*",
Router: "*", Gateway: "*",
Network: "*", Network: "*",
Policy: DiscardIfNone, Policy: DiscardIfNone,
} }
@ -116,12 +116,12 @@ func (q query) String() string {
// create nice table printing structure // create nice table printing structure
table := tablewriter.NewWriter(sb) table := tablewriter.NewWriter(sb)
table.SetHeader([]string{"Destination", "Network", "Router", "Policy"}) table.SetHeader([]string{"Service", "Gateway", "Network", "Policy"})
strQuery := []string{ strQuery := []string{
q.opts.Destination, q.opts.Service,
q.opts.Gateway,
q.opts.Network, q.opts.Network,
q.opts.Router,
fmt.Sprintf("%s", q.opts.Policy), fmt.Sprintf("%s", q.opts.Policy),
} }
table.Append(strQuery) table.Append(strQuery)

View File

@ -9,6 +9,8 @@ import (
) )
var ( var (
// DefaultLink is default network link
DefaultLink = "local"
// DefaultLocalMetric is default route cost metric for the local network // DefaultLocalMetric is default route cost metric for the local network
DefaultLocalMetric = 1 DefaultLocalMetric = 1
// DefaultNetworkMetric is default route cost metric for the micro network // DefaultNetworkMetric is default route cost metric for the micro network
@ -17,14 +19,16 @@ var (
// Route is network route // Route is network route
type Route struct { type Route struct {
// Destination is destination address // Service is destination service name
Destination string Service string
// Address is service node address
Address string
// Gateway is route gateway // Gateway is route gateway
Gateway string Gateway string
// Network is network address // Network is network address
Network string Network string
// Router is the router address // Link is network link
Router string Link string
// Metric is the route cost metric // Metric is the route cost metric
Metric int Metric int
} }
@ -33,7 +37,7 @@ type Route struct {
func (r *Route) Hash() uint64 { func (r *Route) Hash() uint64 {
h := fnv.New64() h := fnv.New64()
h.Reset() h.Reset()
h.Write([]byte(r.Destination + r.Gateway + r.Network)) h.Write([]byte(r.Service + r.Address + r.Gateway + r.Network + r.Link))
return h.Sum64() return h.Sum64()
} }
@ -45,13 +49,14 @@ func (r Route) String() string {
// create nice table printing structure // create nice table printing structure
table := tablewriter.NewWriter(sb) table := tablewriter.NewWriter(sb)
table.SetHeader([]string{"Destination", "Gateway", "Router", "Network", "Metric"}) table.SetHeader([]string{"Service", "Address", "Gateway", "Network", "Link", "Metric"})
strRoute := []string{ strRoute := []string{
r.Destination, r.Service,
r.Address,
r.Gateway, r.Gateway,
r.Router,
r.Network, r.Network,
r.Link,
fmt.Sprintf("%d", r.Metric), fmt.Sprintf("%d", r.Metric),
} }
table.Append(strRoute) table.Append(strRoute)

View File

@ -53,7 +53,7 @@ type Event struct {
// String prints human readable Event // String prints human readable Event
func (e Event) String() string { func (e Event) String() string {
return fmt.Sprintf("[EVENT] %s:\nRoute:\n%s", e.Type, e.Route) return fmt.Sprintf("[EVENT] time: %s type: %s", e.Timestamp, e.Type)
} }
// WatchOption is used to define what routes to watch in the table // WatchOption is used to define what routes to watch in the table
@ -72,15 +72,15 @@ type Watcher interface {
// WatchOptions are table watcher options // WatchOptions are table watcher options
type WatchOptions struct { type WatchOptions struct {
// Specify destination address to watch // Service allows to watch specific service routes
Destination string Service string
} }
// WatchDestination sets what destination to watch // WatchService sets what service routes to watch
// Destination is usually microservice name // Service is the microservice name
func WatchDestination(d string) WatchOption { func WatchService(s string) WatchOption {
return func(o *WatchOptions) { return func(o *WatchOptions) {
o.Destination = d o.Service = s
} }
} }
@ -97,8 +97,8 @@ 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.Service {
case res.Route.Destination, "*": case res.Route.Service, "*":
return res, nil return res, nil
default: default:
log.Logf("no table watcher available to receive the event") log.Logf("no table watcher available to receive the event")
@ -130,10 +130,10 @@ func (w tableWatcher) String() string {
sb := &strings.Builder{} sb := &strings.Builder{}
table := tablewriter.NewWriter(sb) table := tablewriter.NewWriter(sb)
table.SetHeader([]string{"Destination"}) table.SetHeader([]string{"Service"})
data := []string{ data := []string{
w.opts.Destination, w.opts.Service,
} }
table.Append(data) table.Append(data)