From 6beae23afdd9536130dc42894094c960236e5be4 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Tue, 20 Aug 2019 12:48:51 +0100 Subject: [PATCH 01/93] First commit. Outline of the default network. --- network/default.go | 50 +++++++++++++++++++++++++++ network/link/link.go | 11 +++--- network/network.go | 31 +++++++++++++++++ network/options.go | 48 +++++++++++++++++++++++++ network/resolver/dns/dns.go | 1 + network/resolver/http/http.go | 1 + network/resolver/registry/registry.go | 1 + network/resolver/resolver.go | 2 +- router/router.go | 22 ++++++------ 9 files changed, 150 insertions(+), 17 deletions(-) create mode 100644 network/default.go create mode 100644 network/options.go diff --git a/network/default.go b/network/default.go new file mode 100644 index 00000000..29265a86 --- /dev/null +++ b/network/default.go @@ -0,0 +1,50 @@ +package network + +import ( + "github.com/micro/go-micro/client" + "github.com/micro/go-micro/server" +) + +// network implements Network interface +type network struct { + // options configure the network + options Options +} + +// newNetwork returns a new network node +func newNetwork(opts ...Option) Network { + options := DefaultOptions() + + for _, o := range opts { + o(&options) + } + + return &network{ + options: options, + } +} + +// Name returns network name +func (n *network) Name() string { + return n.options.Name +} + +// Connect connects the network +func (n *network) Connect() error { + return nil +} + +// Close closes network connection +func (n *network) Close() error { + return nil +} + +// Client returns network client +func (n *network) Client() client.Client { + return nil +} + +// Server returns network server +func (n *network) Server() server.Server { + return nil +} diff --git a/network/link/link.go b/network/link/link.go index 3acfd772..1c42831a 100644 --- a/network/link/link.go +++ b/network/link/link.go @@ -8,8 +8,13 @@ import ( "github.com/micro/go-micro/transport" ) +var ( + // ErrLinkClosed is returned when attempting i/o operation on the closed link + ErrLinkClosed = errors.New("link closed") +) + // Link is a layer on top of a transport socket with the -// buffering send and recv queue's with the ability to +// buffering send and recv queues with the ability to // measure the actual transport link and reconnect if // an address is specified. type Link interface { @@ -28,10 +33,6 @@ type Link interface { Length() int } -var ( - ErrLinkClosed = errors.New("link closed") -) - // NewLink creates a new link on top of a socket func NewLink(opts ...options.Option) Link { return newLink(options.NewOptions(opts...)) diff --git a/network/network.go b/network/network.go index 15d8155f..44fcbee4 100644 --- a/network/network.go +++ b/network/network.go @@ -1,2 +1,33 @@ // Package network is for creating internetworks package network + +import ( + "github.com/micro/go-micro/client" + "github.com/micro/go-micro/server" +) + +var ( + // DefaultName is default network name + DefaultName = "go.micro.network" + // DefaultAddress is default network address + DefaultAddress = ":0" +) + +// Network is micro network +type Network interface { + // Name of the network + Name() string + // Connect starts the resolver and tunnel server + Connect() error + // Close stops the tunnel and resolving + Close() error + // Client is micro client + Client() client.Client + // Server is micro server + Server() server.Server +} + +// NewNetwork returns a new network interface +func NewNetwork(opts ...Option) Network { + return newNetwork(opts...) +} diff --git a/network/options.go b/network/options.go new file mode 100644 index 00000000..02bc3b1e --- /dev/null +++ b/network/options.go @@ -0,0 +1,48 @@ +package network + +import ( + "github.com/micro/go-micro/network/resolver" + "github.com/micro/go-micro/network/resolver/dns" +) + +type Option func(*Options) + +// Options configure network +type Options struct { + // Name of the network + Name string + // Address to bind to + Address string + // Resolver is network resolver + Resolver resolver.Resolver +} + +// Name is the network name +func Name(n string) Option { + return func(o *Options) { + o.Name = n + } +} + +// Address is the network address +func Address(a string) Option { + return func(o *Options) { + o.Address = a + } +} + +// Resolver is the network resolver +func Resolver(r resolver.Resolver) Option { + return func(o *Options) { + o.Resolver = r + } +} + +// DefaultOptions returns network default options +func DefaultOptions() Options { + return Options{ + Name: DefaultName, + Address: DefaultAddress, + Resolver: &dns.Resolver{}, + } +} diff --git a/network/resolver/dns/dns.go b/network/resolver/dns/dns.go index ba109656..3cfa2746 100644 --- a/network/resolver/dns/dns.go +++ b/network/resolver/dns/dns.go @@ -8,6 +8,7 @@ import ( "github.com/micro/go-micro/network/resolver" ) +// Resolver is a DNS network resolve type Resolver struct{} // Resolve assumes ID is a domain name e.g micro.mu diff --git a/network/resolver/http/http.go b/network/resolver/http/http.go index b6025a59..055662a5 100644 --- a/network/resolver/http/http.go +++ b/network/resolver/http/http.go @@ -10,6 +10,7 @@ import ( "github.com/micro/go-micro/network/resolver" ) +// Resolver is a HTTP network resolver type Resolver struct { // If not set, defaults to http Proto string diff --git a/network/resolver/registry/registry.go b/network/resolver/registry/registry.go index 9fe80bc5..20fffc18 100644 --- a/network/resolver/registry/registry.go +++ b/network/resolver/registry/registry.go @@ -6,6 +6,7 @@ import ( "github.com/micro/go-micro/registry" ) +// Resolver is a registry network resolver type Resolver struct { // Registry is the registry to use otherwise we use the defaul Registry registry.Registry diff --git a/network/resolver/resolver.go b/network/resolver/resolver.go index 2eb0b2a2..369269df 100644 --- a/network/resolver/resolver.go +++ b/network/resolver/resolver.go @@ -5,7 +5,7 @@ package resolver // via the name to connect to. This is done based on Network.Name(). // Before we can be part of any network, we have to connect to it. type Resolver interface { - // Resolve returns a list of addresses for an name + // Resolve returns a list of addresses for a name Resolve(name string) ([]*Record, error) } diff --git a/router/router.go b/router/router.go index 7d7ab0de..c5320590 100644 --- a/router/router.go +++ b/router/router.go @@ -5,6 +5,17 @@ import ( "time" ) +var ( + // DefaultAddress is default router address + DefaultAddress = ":9093" + // DefaultName is default router service name + DefaultName = "go.micro.router" + // DefaultNetwork is default micro network + DefaultNetwork = "go.micro" + // DefaultRouter is default network router + DefaultRouter = NewRouter() +) + // Router is an interface for a routing control plane type Router interface { // Init initializes the router with options @@ -125,17 +136,6 @@ type Advert struct { Events []*Event } -var ( - // DefaultAddress is default router address - DefaultAddress = ":9093" - // DefaultName is default router service name - DefaultName = "go.micro.router" - // DefaultNetwork is default micro network - DefaultNetwork = "go.micro" - // DefaultRouter is default network router - DefaultRouter = NewRouter() -) - // NewRouter creates new Router and returns it func NewRouter(opts ...Option) Router { return newRouter(opts...) From 30dd3f54f0e949655c9d8f89c7ef46c26b850c39 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Tue, 20 Aug 2019 21:11:27 +0100 Subject: [PATCH 02/93] Make router.Table docs consistent --- router/router.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/router/router.go b/router/router.go index c5320590..8b6618d1 100644 --- a/router/router.go +++ b/router/router.go @@ -42,16 +42,17 @@ type Router interface { String() string } +// Table is an interface for routing table type Table interface { // Create new route in the routing table Create(Route) error - // Delete deletes existing route from the routing table + // Delete existing route from the routing table Delete(Route) error - // Update updates route in the routing table + // Update route in the routing table Update(Route) error - // List returns the list of all routes in the table + // List all routes in the table List() ([]Route, error) - // Query queries routes in the routing table + // Query routes in the routing table Query(Query) ([]Route, error) } From fcec6e8eae22f3de9aef377aa0f83fd414e43940 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Tue, 20 Aug 2019 21:12:21 +0100 Subject: [PATCH 03/93] First attempt to implement default network interface --- network/default.go | 296 ++++++++++++++++++++++++++++++++++++++++++++- network/network.go | 4 + network/options.go | 34 ++++++ 3 files changed, 331 insertions(+), 3 deletions(-) diff --git a/network/default.go b/network/default.go index 29265a86..48d52fd5 100644 --- a/network/default.go +++ b/network/default.go @@ -1,14 +1,47 @@ package network import ( + "sync" + "time" + + "github.com/gogo/protobuf/proto" "github.com/micro/go-micro/client" + "github.com/micro/go-micro/proxy" + "github.com/micro/go-micro/router" + pb "github.com/micro/go-micro/router/proto" "github.com/micro/go-micro/server" + "github.com/micro/go-micro/transport" + "github.com/micro/go-micro/tunnel" + tr "github.com/micro/go-micro/tunnel/transport" + "github.com/micro/go-micro/util/log" +) + +var ( + // ControlChannel is the name of the tunnel channel for passing contron message + ControlChannel = "control-msg" ) // network implements Network interface type network struct { // options configure the network + // TODO: we might end up embedding options Options + // rtr is network router + router.Router + // prx is network proxy + proxy.Proxy + // tun is network tunnel + tunnel.Tunnel + // srv is network server + srv server.Server + // client is network client + client client.Client + + sync.RWMutex + // connected marks the network as connected + connected bool + // closed closes the network + closed chan bool } // newNetwork returns a new network node @@ -19,8 +52,33 @@ func newNetwork(opts ...Option) Network { o(&options) } + // init tunnel address to the network bind address + options.Tunnel.Init( + tunnel.Address(options.Address), + ) + + // create tunnel client with tunnel transport + tunTransport := transport.NewTransport( + tr.WithTunnel(options.Tunnel), + ) + + // srv is network server + srv := server.NewServer( + server.Transport(tunTransport), + ) + + // client is network client + client := client.NewClient( + client.Transport(tunTransport), + ) + return &network{ options: options, + Router: options.Router, + Proxy: options.Proxy, + Tunnel: options.Tunnel, + srv: srv, + client: client, } } @@ -29,22 +87,254 @@ func (n *network) Name() string { return n.options.Name } +// Address returns network bind address +func (n *network) Address() string { + return n.options.Address +} + +func (n *network) resolveNodes() ([]string, error) { + // resolve the network address to network nodes + records, err := n.options.Resolver.Resolve(n.options.Name) + if err != nil { + return nil, err + } + + // collect network node addresses + nodes := make([]string, len(records)) + for i, record := range records { + nodes[i] = record.Address + } + + return nodes, nil +} + +func (n *network) resolve() { + resolve := time.NewTicker(ResolveTime) + defer resolve.Stop() + + for { + select { + case <-n.closed: + return + case <-resolve.C: + nodes, err := n.resolveNodes() + if err != nil { + log.Debugf("Network failed to resolve nodes: %v", err) + continue + } + // initialize the tunnel + n.Tunnel.Init( + tunnel.Nodes(nodes...), + ) + } + } +} + +func (n *network) process(client transport.Client) { + for { + m := new(transport.Message) + if err := client.Recv(m); err != nil { + // TODO: should we bail here? + log.Debugf("Network advert receive error: %v", err) + return + } + + // switch on type of message and take action + switch m.Header["Micro-Tunnel"] { + case n.Router.Options().Id: + // NOTE: this should not happen + // skip local adverts + continue + default: + pbAdvert := &pb.Advert{} + if err := proto.Unmarshal(m.Body, pbAdvert); err != nil { + continue + } + + var events []*router.Event + for _, event := range pbAdvert.Events { + route := router.Route{ + Service: event.Route.Service, + Address: event.Route.Address, + Gateway: event.Route.Gateway, + Network: event.Route.Network, + Link: event.Route.Link, + Metric: int(event.Route.Metric), + } + e := &router.Event{ + Type: router.EventType(event.Type), + Timestamp: time.Unix(0, pbAdvert.Timestamp), + Route: route, + } + events = append(events, e) + } + advert := &router.Advert{ + Id: pbAdvert.Id, + Type: router.AdvertType(pbAdvert.Type), + Timestamp: time.Unix(0, pbAdvert.Timestamp), + TTL: time.Duration(pbAdvert.Ttl), + Events: events, + } + + if err := n.Router.Process(advert); err != nil { + log.Debugf("Network failed to process advert %s: %v", advert.Id, err) + continue + } + } + } +} + +// advertise advertises routes to the network +func (n *network) advertise(client transport.Client, advertChan <-chan *router.Advert) { + for { + select { + // process local adverts and randomly fire them at other nodes + case advert := <-advertChan: + // create a proto advert + var events []*pb.Event + for _, event := range advert.Events { + route := &pb.Route{ + Service: event.Route.Service, + Address: event.Route.Address, + Gateway: event.Route.Gateway, + Network: event.Route.Network, + Link: event.Route.Link, + Metric: int64(event.Route.Metric), + } + e := &pb.Event{ + Type: pb.EventType(event.Type), + Timestamp: event.Timestamp.UnixNano(), + Route: route, + } + events = append(events, e) + } + pbAdvert := &pb.Advert{ + Id: advert.Id, + Type: pb.AdvertType(advert.Type), + Timestamp: advert.Timestamp.UnixNano(), + Events: events, + } + body, err := proto.Marshal(pbAdvert) + if err != nil { + // TODO: should we bail here? + log.Debugf("Network failed to marshal message: %v", err) + continue + } + // create transport message and chuck it down the pipe + m := transport.Message{ + Header: map[string]string{ + "Micro-Method": "advert", + }, + Body: body, + } + if err := client.Send(&m); err != nil { + log.Debugf("Network failed to send advert %s: %v", pbAdvert.Id, err) + continue + } + case <-n.closed: + return + } + } +} + // Connect connects the network func (n *network) Connect() error { + n.Lock() + defer n.Unlock() + + // return if already connected + if n.connected { + return nil + } + + // try to resolve network nodes + nodes, err := n.resolveNodes() + if err != nil { + return err + } + + // connect network tunnel + if err := n.Tunnel.Connect(); err != nil { + return err + } + + // initialize the tunnel to resolved nodes + n.Tunnel.Init( + tunnel.Nodes(nodes...), + ) + + // dial into ControlChannel to send route adverts + client, err := n.Tunnel.Dial(ControlChannel) + if err != nil { + // TODO: should we stop the tunnel here? + return err + } + + // create closed channel + n.closed = make(chan bool) + + // keep resolving network nodes + go n.resolve() + + // TODO: do we assume the router has been started? + // start advertising routes + advertChan, err := n.options.Router.Advertise() + if err != nil { + return err + } + + // advertise routes + go n.advertise(client, advertChan) + // process routes + go n.process(client) + + // set connected to true + n.connected = true + + return nil +} + +func (n *network) close() error { + // stop the router + if err := n.Router.Stop(); err != nil { + return err + } + + // close the tunnel + if err := n.Tunnel.Close(); err != nil { + return err + } + return nil } // Close closes network connection func (n *network) Close() error { - return nil + n.Lock() + defer n.Unlock() + + if !n.connected { + return nil + } + + select { + case <-n.closed: + return nil + default: + close(n.closed) + // set connected to false + n.connected = false + } + + return n.close() } // Client returns network client func (n *network) Client() client.Client { - return nil + return n.client } // Server returns network server func (n *network) Server() server.Server { - return nil + return n.srv } diff --git a/network/network.go b/network/network.go index 44fcbee4..0b018d44 100644 --- a/network/network.go +++ b/network/network.go @@ -2,6 +2,8 @@ package network import ( + "time" + "github.com/micro/go-micro/client" "github.com/micro/go-micro/server" ) @@ -11,6 +13,8 @@ var ( DefaultName = "go.micro.network" // DefaultAddress is default network address DefaultAddress = ":0" + // ResolveTime ddefines the time we periodically resolve network nodes + ResolveTime = 1 * time.Minute ) // Network is micro network diff --git a/network/options.go b/network/options.go index 02bc3b1e..4c737fe1 100644 --- a/network/options.go +++ b/network/options.go @@ -3,6 +3,10 @@ package network import ( "github.com/micro/go-micro/network/resolver" "github.com/micro/go-micro/network/resolver/dns" + "github.com/micro/go-micro/proxy" + "github.com/micro/go-micro/proxy/mucp" + "github.com/micro/go-micro/router" + "github.com/micro/go-micro/tunnel" ) type Option func(*Options) @@ -13,6 +17,12 @@ type Options struct { Name string // Address to bind to Address string + // Tunnel is network tunnel + Tunnel tunnel.Tunnel + // Router is network router + Router router.Router + // Proxy is network proxy + Proxy proxy.Proxy // Resolver is network resolver Resolver resolver.Resolver } @@ -31,6 +41,27 @@ func Address(a string) Option { } } +// Tunnel sets the network tunnel +func Tunnel(t tunnel.Tunnel) Option { + return func(o *Options) { + o.Tunnel = t + } +} + +// Router sets the network router +func Router(r router.Router) Option { + return func(o *Options) { + o.Router = r + } +} + +// Proxy sets the network proxy +func Proxy(p proxy.Proxy) Option { + return func(o *Options) { + o.Proxy = p + } +} + // Resolver is the network resolver func Resolver(r resolver.Resolver) Option { return func(o *Options) { @@ -43,6 +74,9 @@ func DefaultOptions() Options { return Options{ Name: DefaultName, Address: DefaultAddress, + Tunnel: tunnel.NewTunnel(), + Router: router.DefaultRouter, + Proxy: mucp.NewProxy(), Resolver: &dns.Resolver{}, } } From a6e1287b27fb91eee371774fb3aae224292ecb7c Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Tue, 20 Aug 2019 21:15:02 +0100 Subject: [PATCH 04/93] Replaced incorrect proto import path --- network/default.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/default.go b/network/default.go index 48d52fd5..ed23a7d7 100644 --- a/network/default.go +++ b/network/default.go @@ -4,7 +4,7 @@ import ( "sync" "time" - "github.com/gogo/protobuf/proto" + "github.com/golang/protobuf/proto" "github.com/micro/go-micro/client" "github.com/micro/go-micro/proxy" "github.com/micro/go-micro/router" From 6c1f1d66f7b9bd98189a18f5d7984683b963ad28 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Tue, 20 Aug 2019 21:30:25 +0100 Subject: [PATCH 05/93] Switch received messages on the right header --- network/default.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/network/default.go b/network/default.go index ed23a7d7..d5f3f8ce 100644 --- a/network/default.go +++ b/network/default.go @@ -140,12 +140,8 @@ func (n *network) process(client transport.Client) { } // switch on type of message and take action - switch m.Header["Micro-Tunnel"] { - case n.Router.Options().Id: - // NOTE: this should not happen - // skip local adverts - continue - default: + switch m.Header["Micro-Method"] { + case "advert": pbAdvert := &pb.Advert{} if err := proto.Unmarshal(m.Body, pbAdvert); err != nil { continue From a09d5d2e9af37a12da3618096f60f8c99e99e11b Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Wed, 21 Aug 2019 19:16:18 +0100 Subject: [PATCH 06/93] Add Address method. Start and Stop router/server. --- network/default.go | 31 +++++++++++++++++++++++++------ network/network.go | 4 +++- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/network/default.go b/network/default.go index d5f3f8ce..ba31d7c5 100644 --- a/network/default.go +++ b/network/default.go @@ -6,25 +6,25 @@ import ( "github.com/golang/protobuf/proto" "github.com/micro/go-micro/client" + rtr "github.com/micro/go-micro/client/selector/router" "github.com/micro/go-micro/proxy" "github.com/micro/go-micro/router" pb "github.com/micro/go-micro/router/proto" "github.com/micro/go-micro/server" "github.com/micro/go-micro/transport" "github.com/micro/go-micro/tunnel" - tr "github.com/micro/go-micro/tunnel/transport" + trn "github.com/micro/go-micro/tunnel/transport" "github.com/micro/go-micro/util/log" ) var ( // ControlChannel is the name of the tunnel channel for passing contron message - ControlChannel = "control-msg" + ControlChannel = "control" ) // network implements Network interface type network struct { // options configure the network - // TODO: we might end up embedding options Options // rtr is network router router.Router @@ -59,7 +59,7 @@ func newNetwork(opts ...Option) Network { // create tunnel client with tunnel transport tunTransport := transport.NewTransport( - tr.WithTunnel(options.Tunnel), + trn.WithTunnel(options.Tunnel), ) // srv is network server @@ -70,6 +70,11 @@ func newNetwork(opts ...Option) Network { // client is network client client := client.NewClient( client.Transport(tunTransport), + client.Selector( + rtr.NewSelector( + rtr.WithRouter(options.Router), + ), + ), ) return &network{ @@ -89,7 +94,7 @@ func (n *network) Name() string { // Address returns network bind address func (n *network) Address() string { - return n.options.Address + return n.Tunnel.Address() } func (n *network) resolveNodes() ([]string, error) { @@ -272,7 +277,11 @@ func (n *network) Connect() error { // keep resolving network nodes go n.resolve() - // TODO: do we assume the router has been started? + // start the router + if err := n.options.Router.Start(); err != nil { + return err + } + // start advertising routes advertChan, err := n.options.Router.Advertise() if err != nil { @@ -284,6 +293,11 @@ func (n *network) Connect() error { // process routes go n.process(client) + // start the server + if err := n.srv.Start(); err != nil { + return err + } + // set connected to true n.connected = true @@ -291,6 +305,11 @@ func (n *network) Connect() error { } func (n *network) close() error { + // stop the server + if err := n.srv.Stop(); err != nil { + return err + } + // stop the router if err := n.Router.Stop(); err != nil { return err diff --git a/network/network.go b/network/network.go index 0b018d44..3663876f 100644 --- a/network/network.go +++ b/network/network.go @@ -13,7 +13,7 @@ var ( DefaultName = "go.micro.network" // DefaultAddress is default network address DefaultAddress = ":0" - // ResolveTime ddefines the time we periodically resolve network nodes + // ResolveTime ddefines the time to periodically resolve network nodes ResolveTime = 1 * time.Minute ) @@ -21,6 +21,8 @@ var ( type Network interface { // Name of the network Name() string + // Address returns network bind address + Address() string // Connect starts the resolver and tunnel server Connect() error // Close stops the tunnel and resolving From e1599b0f17fc81f77a5500d8a0288410993476ff Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Wed, 21 Aug 2019 19:21:23 +0100 Subject: [PATCH 07/93] Set server name. Set default network name. --- network/default.go | 1 + network/network.go | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/network/default.go b/network/default.go index ba31d7c5..969aed5f 100644 --- a/network/default.go +++ b/network/default.go @@ -64,6 +64,7 @@ func newNetwork(opts ...Option) Network { // srv is network server srv := server.NewServer( + server.Name(DefaultName), server.Transport(tunTransport), ) diff --git a/network/network.go b/network/network.go index 3663876f..46801e0c 100644 --- a/network/network.go +++ b/network/network.go @@ -10,7 +10,7 @@ import ( var ( // DefaultName is default network name - DefaultName = "go.micro.network" + DefaultName = "go.micro" // DefaultAddress is default network address DefaultAddress = ":0" // ResolveTime ddefines the time to periodically resolve network nodes From db89fc4efeee172ebd6726cd0ec7cd6fe9807bd1 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Wed, 21 Aug 2019 19:22:50 +0100 Subject: [PATCH 08/93] Set server name to the correct value. --- network/default.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/default.go b/network/default.go index 969aed5f..56f82a17 100644 --- a/network/default.go +++ b/network/default.go @@ -64,7 +64,7 @@ func newNetwork(opts ...Option) Network { // srv is network server srv := server.NewServer( - server.Name(DefaultName), + server.Name(options.Name), server.Transport(tunTransport), ) From e53484302ce6d1c0b83622c4702e24c5ae8dbd3f Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Thu, 22 Aug 2019 13:17:32 +0100 Subject: [PATCH 09/93] Added ControlChannel tunnel.Listener to process incoming messages --- network/default.go | 107 +++++++++++++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 37 deletions(-) diff --git a/network/default.go b/network/default.go index 56f82a17..bab14f13 100644 --- a/network/default.go +++ b/network/default.go @@ -136,52 +136,80 @@ func (n *network) resolve() { } } -func (n *network) process(client transport.Client) { +func (n *network) handleConn(conn tunnel.Conn, msg chan *transport.Message) { for { m := new(transport.Message) - if err := client.Recv(m); err != nil { + if err := conn.Recv(m); err != nil { // TODO: should we bail here? - log.Debugf("Network advert receive error: %v", err) + log.Debugf("Network tunnel advert receive error: %v", err) return } - // switch on type of message and take action - switch m.Header["Micro-Method"] { - case "advert": - pbAdvert := &pb.Advert{} - if err := proto.Unmarshal(m.Body, pbAdvert); err != nil { - continue - } + select { + case msg <- m: + case <-n.closed: + return + } + } +} - var events []*router.Event - for _, event := range pbAdvert.Events { - route := router.Route{ - Service: event.Route.Service, - Address: event.Route.Address, - Gateway: event.Route.Gateway, - Network: event.Route.Network, - Link: event.Route.Link, - Metric: int(event.Route.Metric), +func (n *network) process(l tunnel.Listener) { + // receive control message queue + recv := make(chan *transport.Message, 128) + + // accept a connection + conn, err := l.Accept() + if err != nil { + // TODO: handle this + log.Debugf("Network tunnel accept error: %v", err) + return + } + + go n.handleConn(conn, recv) + + for { + select { + case m := <-recv: + // switch on type of message and take action + switch m.Header["Micro-Method"] { + case "advert": + pbAdvert := &pb.Advert{} + if err := proto.Unmarshal(m.Body, pbAdvert); err != nil { + continue } - e := &router.Event{ - Type: router.EventType(event.Type), + + var events []*router.Event + for _, event := range pbAdvert.Events { + route := router.Route{ + Service: event.Route.Service, + Address: event.Route.Address, + Gateway: event.Route.Gateway, + Network: event.Route.Network, + Link: event.Route.Link, + Metric: int(event.Route.Metric), + } + e := &router.Event{ + Type: router.EventType(event.Type), + Timestamp: time.Unix(0, pbAdvert.Timestamp), + Route: route, + } + events = append(events, e) + } + advert := &router.Advert{ + Id: pbAdvert.Id, + Type: router.AdvertType(pbAdvert.Type), Timestamp: time.Unix(0, pbAdvert.Timestamp), - Route: route, + TTL: time.Duration(pbAdvert.Ttl), + Events: events, } - events = append(events, e) - } - advert := &router.Advert{ - Id: pbAdvert.Id, - Type: router.AdvertType(pbAdvert.Type), - Timestamp: time.Unix(0, pbAdvert.Timestamp), - TTL: time.Duration(pbAdvert.Ttl), - Events: events, - } - if err := n.Router.Process(advert); err != nil { - log.Debugf("Network failed to process advert %s: %v", advert.Id, err) - continue + if err := n.Router.Process(advert); err != nil { + log.Debugf("Network failed to process advert %s: %v", advert.Id, err) + continue + } } + case <-n.closed: + return } } } @@ -268,7 +296,12 @@ func (n *network) Connect() error { // dial into ControlChannel to send route adverts client, err := n.Tunnel.Dial(ControlChannel) if err != nil { - // TODO: should we stop the tunnel here? + return err + } + + // listen on ControlChannel + listener, err := n.Tunnel.Listen(ControlChannel) + if err != nil { return err } @@ -291,8 +324,8 @@ func (n *network) Connect() error { // advertise routes go n.advertise(client, advertChan) - // process routes - go n.process(client) + // accept and process routes + go n.process(listener) // start the server if err := n.srv.Start(); err != nil { From 8c3eec9f2aced5d057ad63a100fc3131dbd589c7 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Thu, 22 Aug 2019 18:57:20 +0100 Subject: [PATCH 10/93] Set the default resolver to registry --- network/options.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/options.go b/network/options.go index 4c737fe1..8bc36972 100644 --- a/network/options.go +++ b/network/options.go @@ -2,7 +2,7 @@ package network import ( "github.com/micro/go-micro/network/resolver" - "github.com/micro/go-micro/network/resolver/dns" + "github.com/micro/go-micro/network/resolver/registry" "github.com/micro/go-micro/proxy" "github.com/micro/go-micro/proxy/mucp" "github.com/micro/go-micro/router" @@ -77,6 +77,6 @@ func DefaultOptions() Options { Tunnel: tunnel.NewTunnel(), Router: router.DefaultRouter, Proxy: mucp.NewProxy(), - Resolver: &dns.Resolver{}, + Resolver: ®istry.Resolver{}, } } From 9448d7c164411fa325a55f1344bb9d141261da65 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Fri, 23 Aug 2019 16:01:57 +0100 Subject: [PATCH 11/93] Set Route.Network to "network" and Router.Gateway to network.Address --- network/default.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/network/default.go b/network/default.go index bab14f13..e4512172 100644 --- a/network/default.go +++ b/network/default.go @@ -226,8 +226,8 @@ func (n *network) advertise(client transport.Client, advertChan <-chan *router.A route := &pb.Route{ Service: event.Route.Service, Address: event.Route.Address, - Gateway: event.Route.Gateway, - Network: event.Route.Network, + Gateway: n.options.Address, + Network: "network", Link: event.Route.Link, Metric: int64(event.Route.Metric), } @@ -257,6 +257,7 @@ func (n *network) advertise(client transport.Client, advertChan <-chan *router.A }, Body: body, } + if err := client.Send(&m); err != nil { log.Debugf("Network failed to send advert %s: %v", pbAdvert.Id, err) continue From 88e47b9b066d45d1f6230c9693396ef3273f8b2e Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Fri, 23 Aug 2019 17:48:14 +0100 Subject: [PATCH 12/93] Dont bail when unable to resolve network nodes. --- network/default.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/default.go b/network/default.go index e4512172..c7f6a3ab 100644 --- a/network/default.go +++ b/network/default.go @@ -281,7 +281,7 @@ func (n *network) Connect() error { // try to resolve network nodes nodes, err := n.resolveNodes() if err != nil { - return err + log.Debugf("Network failed to resolve nodes: %v", err) } // connect network tunnel From ed8d28c9ab4eaf4a87689722756ccd328d251b8a Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Fri, 23 Aug 2019 21:08:18 +0100 Subject: [PATCH 13/93] Set Route.Link to "network" not Route.Network. Oops! --- network/default.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/network/default.go b/network/default.go index c7f6a3ab..be49f9f8 100644 --- a/network/default.go +++ b/network/default.go @@ -223,12 +223,13 @@ func (n *network) advertise(client transport.Client, advertChan <-chan *router.A // create a proto advert var events []*pb.Event for _, event := range advert.Events { + // NOTE: we override the Gateway and Link fields here route := &pb.Route{ Service: event.Route.Service, Address: event.Route.Address, Gateway: n.options.Address, - Network: "network", - Link: event.Route.Link, + Network: event.Route.Network, + Link: "network", Metric: int64(event.Route.Metric), } e := &pb.Event{ From 35e7b9551f26f16c6ed118c3f6bf9042d9bb1ba2 Mon Sep 17 00:00:00 2001 From: huanghaoyan Date: Mon, 26 Aug 2019 14:37:49 +0800 Subject: [PATCH 14/93] ignore Loopback Address (LVS,DR mode) --- util/addr/addr.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/util/addr/addr.go b/util/addr/addr.go index b2874533..36a78f92 100644 --- a/util/addr/addr.go +++ b/util/addr/addr.go @@ -46,6 +46,9 @@ func Extract(addr string) (string, error) { // ignore error, interface can dissapear from system continue } + if iface.Flags&net.FlagLoopback != 0 { + continue + } addrs = append(addrs, ifaceAddrs...) } From 0888d2fbbce9d2800230b6f124bd756b0b24352b Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Tue, 27 Aug 2019 08:13:58 +0100 Subject: [PATCH 15/93] Add grpc content-type --- client/grpc/codec.go | 1 + 1 file changed, 1 insertion(+) diff --git a/client/grpc/codec.go b/client/grpc/codec.go index e7891eef..c792abbd 100644 --- a/client/grpc/codec.go +++ b/client/grpc/codec.go @@ -30,6 +30,7 @@ var ( "application/proto": protoCodec{}, "application/protobuf": protoCodec{}, "application/octet-stream": protoCodec{}, + "application/grpc": protoCodec{}, "application/grpc+json": jsonCodec{}, "application/grpc+proto": protoCodec{}, "application/grpc+bytes": bytesCodec{}, From 371b23d0559022f1980ab269846b2773939a9c28 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Tue, 27 Aug 2019 11:36:46 +0100 Subject: [PATCH 16/93] Introduce DefaultLink; dont hardcode name of the link --- network/default.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/network/default.go b/network/default.go index be49f9f8..615d567e 100644 --- a/network/default.go +++ b/network/default.go @@ -20,6 +20,8 @@ import ( var ( // ControlChannel is the name of the tunnel channel for passing contron message ControlChannel = "control" + // DefaultLink is default network link + DefaultLink = "network" ) // network implements Network interface @@ -229,7 +231,7 @@ func (n *network) advertise(client transport.Client, advertChan <-chan *router.A Address: event.Route.Address, Gateway: n.options.Address, Network: event.Route.Network, - Link: "network", + Link: DefaultLink, Metric: int64(event.Route.Metric), } e := &pb.Event{ From 87b56d46ac72526f9e2248ad2c18c78acf2d0501 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Tue, 27 Aug 2019 13:21:36 +0100 Subject: [PATCH 17/93] Use tunnel transport and set server address --- network/default.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/network/default.go b/network/default.go index 615d567e..9aaff91a 100644 --- a/network/default.go +++ b/network/default.go @@ -60,12 +60,13 @@ func newNetwork(opts ...Option) Network { ) // create tunnel client with tunnel transport - tunTransport := transport.NewTransport( + tunTransport := trn.NewTransport( trn.WithTunnel(options.Tunnel), ) // srv is network server srv := server.NewServer( + server.Address(options.Address), server.Name(options.Name), server.Transport(tunTransport), ) From a6ab4d7b4b89673e301c35905e8677270b902a10 Mon Sep 17 00:00:00 2001 From: huanghaoyan Date: Tue, 27 Aug 2019 23:33:30 +0800 Subject: [PATCH 18/93] check last for the address bind in lo interface. --- util/addr/addr.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/util/addr/addr.go b/util/addr/addr.go index 36a78f92..911dacb0 100644 --- a/util/addr/addr.go +++ b/util/addr/addr.go @@ -40,6 +40,7 @@ func Extract(addr string) (string, error) { } var addrs []net.Addr + var loAddrs []net.Addr for _, iface := range ifaces { ifaceAddrs, err := iface.Addrs() if err != nil { @@ -47,10 +48,12 @@ func Extract(addr string) (string, error) { continue } if iface.Flags&net.FlagLoopback != 0 { + loAddrs = append(loAddrs, ifaceAddrs...) continue } addrs = append(addrs, ifaceAddrs...) } + addrs = append(addrs, loAddrs...) var ipAddr []byte var publicIP []byte From 5e7208119efdc48140735d2d7691b1bf13018ff6 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Tue, 27 Aug 2019 23:08:35 +0100 Subject: [PATCH 19/93] Adds network id. Skips processing routes when router is the origin. --- network/default.go | 22 ++++++++++------ network/options.go | 15 +++++++++-- router/default.go | 29 +++++++++++++-------- router/query.go | 21 +++++++++------ router/route.go | 8 +++--- router/table.go | 62 +++++++++++++++++++++++--------------------- router/table_test.go | 43 +++++++++++++++++++++++++----- router/watcher.go | 18 +++++++------ 8 files changed, 142 insertions(+), 76 deletions(-) diff --git a/network/default.go b/network/default.go index 9aaff91a..951221ba 100644 --- a/network/default.go +++ b/network/default.go @@ -34,8 +34,8 @@ type network struct { proxy.Proxy // tun is network tunnel tunnel.Tunnel - // srv is network server - srv server.Server + // server is network server + server server.Server // client is network client client client.Client @@ -59,13 +59,19 @@ func newNetwork(opts ...Option) Network { tunnel.Address(options.Address), ) + // init router Id to the network id + options.Router.Init( + router.Id(options.Id), + ) + // create tunnel client with tunnel transport tunTransport := trn.NewTransport( trn.WithTunnel(options.Tunnel), ) - // srv is network server - srv := server.NewServer( + // server is network server + server := server.NewServer( + server.Id(options.Id), server.Address(options.Address), server.Name(options.Name), server.Transport(tunTransport), @@ -86,7 +92,7 @@ func newNetwork(opts ...Option) Network { Router: options.Router, Proxy: options.Proxy, Tunnel: options.Tunnel, - srv: srv, + server: server, client: client, } } @@ -333,7 +339,7 @@ func (n *network) Connect() error { go n.process(listener) // start the server - if err := n.srv.Start(); err != nil { + if err := n.server.Start(); err != nil { return err } @@ -345,7 +351,7 @@ func (n *network) Connect() error { func (n *network) close() error { // stop the server - if err := n.srv.Stop(); err != nil { + if err := n.server.Stop(); err != nil { return err } @@ -390,5 +396,5 @@ func (n *network) Client() client.Client { // Server returns network server func (n *network) Server() server.Server { - return n.srv + return n.server } diff --git a/network/options.go b/network/options.go index 8bc36972..5894b857 100644 --- a/network/options.go +++ b/network/options.go @@ -1,6 +1,7 @@ package network import ( + "github.com/google/uuid" "github.com/micro/go-micro/network/resolver" "github.com/micro/go-micro/network/resolver/registry" "github.com/micro/go-micro/proxy" @@ -13,6 +14,8 @@ type Option func(*Options) // Options configure network type Options struct { + // Id of the node + Id string // Name of the network Name string // Address to bind to @@ -27,14 +30,21 @@ type Options struct { Resolver resolver.Resolver } -// Name is the network name +// Id sets the id of the network node +func Id(id string) Option { + return func(o *Options) { + o.Id = id + } +} + +// Name sets the network name func Name(n string) Option { return func(o *Options) { o.Name = n } } -// Address is the network address +// Address sets the network address func Address(a string) Option { return func(o *Options) { o.Address = a @@ -72,6 +82,7 @@ func Resolver(r resolver.Resolver) Option { // DefaultOptions returns network default options func DefaultOptions() Options { return Options{ + Id: uuid.New().String(), Name: DefaultName, Address: DefaultAddress, Tunnel: tunnel.NewTunnel(), diff --git a/router/default.go b/router/default.go index bf3f0e60..05e033ce 100644 --- a/router/default.go +++ b/router/default.go @@ -43,7 +43,7 @@ var ( // router implements default router type router struct { sync.RWMutex - opts Options + options Options status Status table *table exit chan struct{} @@ -70,7 +70,7 @@ func newRouter(opts ...Option) Router { status := Status{Code: Stopped, Error: nil} return &router{ - opts: options, + options: options, status: status, table: newTable(), advertWg: &sync.WaitGroup{}, @@ -85,7 +85,7 @@ func (r *router) Init(opts ...Option) error { defer r.Unlock() for _, o := range opts { - o(&r.opts) + o(&r.options) } return nil @@ -94,10 +94,10 @@ func (r *router) Init(opts ...Option) error { // Options returns router options func (r *router) Options() Options { r.Lock() - opts := r.opts + options := r.options r.Unlock() - return opts + return options } // Table returns routing table @@ -139,7 +139,8 @@ func (r *router) manageServiceRoutes(service *registry.Service, action string) e Service: service.Name, Address: node.Address, Gateway: "", - Network: r.opts.Network, + Network: r.options.Network, + Router: r.options.Id, Link: DefaultLink, Metric: DefaultLocalMetric, } @@ -278,7 +279,7 @@ func (r *router) publishAdvert(advType AdvertType, events []*Event) { defer r.advertWg.Done() a := &Advert{ - Id: r.opts.Id, + Id: r.options.Id, Type: advType, TTL: DefaultAdvertTTL, Timestamp: time.Now(), @@ -529,20 +530,22 @@ func (r *router) Start() error { } // add all local service routes into the routing table - if err := r.manageRegistryRoutes(r.opts.Registry, "create"); err != nil { + if err := r.manageRegistryRoutes(r.options.Registry, "create"); err != nil { e := fmt.Errorf("failed adding registry routes: %s", err) r.status = Status{Code: Error, Error: e} return e } // add default gateway into routing table - if r.opts.Gateway != "" { + if r.options.Gateway != "" { // note, the only non-default value is the gateway route := Route{ Service: "*", Address: "*", - Gateway: r.opts.Gateway, + Gateway: r.options.Gateway, Network: "*", + Router: r.options.Id, + Link: DefaultLink, Metric: DefaultLocalMetric, } if err := r.table.Create(route); err != nil { @@ -557,7 +560,7 @@ func (r *router) Start() error { r.exit = make(chan struct{}) // registry watcher - regWatcher, err := r.opts.Registry.Watch() + regWatcher, err := r.options.Registry.Watch() if err != nil { e := fmt.Errorf("failed creating registry watcher: %v", err) r.status = Status{Code: Error, Error: e} @@ -669,6 +672,10 @@ func (r *router) Process(a *Advert) error { }) for _, event := range events { + // skip if the router is the origin of this route + if event.Route.Router == r.options.Id { + continue + } // create a copy of the route route := event.Route action := event.Type diff --git a/router/query.go b/router/query.go index 68fb3588..3e2c3d38 100644 --- a/router/query.go +++ b/router/query.go @@ -11,29 +11,38 @@ type QueryOptions struct { Gateway string // Network is network address Network string + // Router is router id + Router string } -// QueryService sets destination address +// QueryService sets service to query func QueryService(s string) QueryOption { return func(o *QueryOptions) { o.Service = s } } -// QueryGateway sets route gateway +// QueryGateway sets gateway address to query func QueryGateway(g string) QueryOption { return func(o *QueryOptions) { o.Gateway = g } } -// QueryNetwork sets route network address +// QueryNetwork sets network name to query func QueryNetwork(n string) QueryOption { return func(o *QueryOptions) { o.Network = n } } +// QueryRouter sets router id to query +func QueryRouter(r string) QueryOption { + return func(o *QueryOptions) { + o.Router = r + } +} + // Query is routing table query type Query interface { // Options returns query options @@ -52,6 +61,7 @@ func NewQuery(opts ...QueryOption) Query { Service: "*", Gateway: "*", Network: "*", + Router: "*", } for _, o := range opts { @@ -67,8 +77,3 @@ func NewQuery(opts ...QueryOption) Query { func (q *query) Options() QueryOptions { return q.opts } - -// String prints routing table query in human readable form -func (q query) String() string { - return "query" -} diff --git a/router/route.go b/router/route.go index fa9dbc72..f2768403 100644 --- a/router/route.go +++ b/router/route.go @@ -7,9 +7,9 @@ import ( var ( // DefaultLink is default network link DefaultLink = "local" - // DefaultLocalMetric is default route cost metric for the local network + // DefaultLocalMetric is default route cost for a local route DefaultLocalMetric = 1 - // DefaultNetworkMetric is default route cost metric for the micro network + // DefaultNetworkMetric is default route cost for a network route DefaultNetworkMetric = 10 ) @@ -23,6 +23,8 @@ type Route struct { Gateway string // Network is network address Network string + // Router is router id + Router string // Link is network link Link string // Metric is the route cost metric @@ -33,6 +35,6 @@ type Route struct { func (r *Route) Hash() uint64 { h := fnv.New64() h.Reset() - h.Write([]byte(r.Service + r.Address + r.Gateway + r.Network + r.Link)) + h.Write([]byte(r.Service + r.Address + r.Gateway + r.Network + r.Router + r.Link)) return h.Sum64() } diff --git a/router/table.go b/router/table.go index 6b34bb00..42a1be47 100644 --- a/router/table.go +++ b/router/table.go @@ -8,7 +8,14 @@ import ( "github.com/google/uuid" ) -// table is an in memory routing table +var ( + // ErrRouteNotFound is returned when no route was found in the routing table + ErrRouteNotFound = errors.New("route not found") + // ErrDuplicateRoute is returned when the route already exists + ErrDuplicateRoute = errors.New("duplicate route") +) + +// table is an in-memory routing table type table struct { sync.RWMutex // routes stores service routes @@ -25,6 +32,19 @@ func newTable(opts ...Option) *table { } } +// sendEvent sends events to all subscribed watchers +func (t *table) sendEvent(e *Event) { + t.RLock() + defer t.RUnlock() + + for _, w := range t.watchers { + select { + case w.resChan <- e: + case <-w.done: + } + } +} + // Create creates new route in the routing table func (t *table) Create(r Route) error { service := r.Service @@ -106,21 +126,23 @@ func (t *table) List() ([]Route, error) { return routes, nil } -// isMatch checks if the route matches given network and router -func isMatch(route Route, network, router string) bool { - if network == "*" || network == route.Network { - if router == "*" || router == route.Gateway { - return true +// isMatch checks if the route matches given query options +func isMatch(route Route, gateway, network, router string) bool { + if gateway == "*" || gateway == route.Gateway { + if network == "*" || network == route.Network { + if router == "*" || router == route.Router { + return true + } } } return false } // findRoutes finds all the routes for given network and router and returns them -func findRoutes(routes map[uint64]Route, network, router string) []Route { +func findRoutes(routes map[uint64]Route, gateway, network, router string) []Route { var results []Route for _, route := range routes { - if isMatch(route, network, router) { + if isMatch(route, gateway, network, router) { results = append(results, route) } } @@ -136,13 +158,13 @@ func (t *table) Query(q Query) ([]Route, error) { if _, ok := t.routes[q.Options().Service]; !ok { return nil, ErrRouteNotFound } - return findRoutes(t.routes[q.Options().Service], q.Options().Network, q.Options().Gateway), nil + return findRoutes(t.routes[q.Options().Service], q.Options().Gateway, q.Options().Network, q.Options().Router), nil } var results []Route // search through all destinations for _, routes := range t.routes { - results = append(results, findRoutes(routes, q.Options().Network, q.Options().Gateway)...) + results = append(results, findRoutes(routes, q.Options().Gateway, q.Options().Network, q.Options().Router)...) } return results, nil @@ -181,23 +203,3 @@ func (t *table) Watch(opts ...WatchOption) (Watcher, error) { return w, nil } - -// sendEvent sends events to all subscribed watchers -func (t *table) sendEvent(e *Event) { - t.RLock() - defer t.RUnlock() - - for _, w := range t.watchers { - select { - case w.resChan <- e: - case <-w.done: - } - } -} - -var ( - // ErrRouteNotFound is returned when no route was found in the routing table - ErrRouteNotFound = errors.New("route not found") - // ErrDuplicateRoute is returned when the route already exists - ErrDuplicateRoute = errors.New("duplicate route") -) diff --git a/router/table_test.go b/router/table_test.go index 16c73f2f..d989ee3b 100644 --- a/router/table_test.go +++ b/router/table_test.go @@ -9,6 +9,7 @@ func testSetup() (*table, Route) { Service: "dest.svc", Gateway: "dest.gw", Network: "dest.network", + Router: "src.router", Link: "det.link", Metric: 10, } @@ -109,11 +110,13 @@ func TestQuery(t *testing.T) { svc := []string{"svc1", "svc2", "svc3"} net := []string{"net1", "net2", "net1"} gw := []string{"gw1", "gw2", "gw3"} + rtr := []string{"rtr1", "rt2", "rt3"} for i := 0; i < len(svc); i++ { route.Service = svc[i] route.Network = net[i] route.Gateway = gw[i] + route.Router = rtr[i] if err := table.Create(route); err != nil { t.Errorf("error adding route: %s", err) } @@ -127,8 +130,9 @@ func TestQuery(t *testing.T) { t.Errorf("error looking up routes: %s", err) } - // query particular net - query = NewQuery(QueryNetwork("net1")) + // query routes particular network + network := "net1" + query = NewQuery(QueryNetwork(network)) routes, err = table.Query(query) if err != nil { @@ -139,7 +143,13 @@ func TestQuery(t *testing.T) { t.Errorf("incorrect number of routes returned. Expected: %d, found: %d", 2, len(routes)) } - // query particular gateway + for _, route := range routes { + if route.Network != network { + t.Errorf("incorrect route returned. Expected network: %s, found: %s", network, route.Network) + } + } + + // query routes for particular gateway gateway := "gw1" query = NewQuery(QueryGateway(gateway)) @@ -156,11 +166,28 @@ func TestQuery(t *testing.T) { t.Errorf("incorrect route returned. Expected gateway: %s, found: %s", gateway, routes[0].Gateway) } - // query particular route - network := "net1" + // query routes for particular router + router := "rtr1" + query = NewQuery(QueryRouter(router)) + + routes, err = table.Query(query) + if err != nil { + t.Errorf("error looking up routes: %s", err) + } + + if len(routes) != 1 { + t.Errorf("incorrect number of routes returned. Expected: %d, found: %d", 1, len(routes)) + } + + if routes[0].Router != router { + t.Errorf("incorrect route returned. Expected router: %s, found: %s", router, routes[0].Router) + } + + // query particular gateway and network query = NewQuery( QueryGateway(gateway), QueryNetwork(network), + QueryRouter(router), ) routes, err = table.Query(query) @@ -180,7 +207,11 @@ func TestQuery(t *testing.T) { t.Errorf("incorrect network returned. Expected network: %s, found: %s", network, routes[0].Network) } - // bullshit route query + if routes[0].Router != router { + t.Errorf("incorrect route returned. Expected router: %s, found: %s", router, routes[0].Router) + } + + // non-existen route query query = NewQuery(QueryService("foobar")) routes, err = table.Query(query) diff --git a/router/watcher.go b/router/watcher.go index f2c8beac..148b88d4 100644 --- a/router/watcher.go +++ b/router/watcher.go @@ -6,6 +6,11 @@ import ( "time" ) +var ( + // ErrWatcherStopped is returned when routing table watcher has been stopped + ErrWatcherStopped = errors.New("watcher stopped") +) + // EventType defines routing table event type EventType int @@ -42,9 +47,6 @@ type Event struct { Route Route } -// WatchOption is used to define what routes to watch in the table -type WatchOption func(*WatchOptions) - // Watcher defines routing table watcher interface // Watcher returns updates to the routing table type Watcher interface { @@ -56,7 +58,11 @@ type Watcher interface { Stop() } +// WatchOption is used to define what routes to watch in the table +type WatchOption func(*WatchOptions) + // WatchOptions are table watcher options +// TODO: expand the options to watch based on other criteria type WatchOptions struct { // Service allows to watch specific service routes Service string @@ -70,6 +76,7 @@ func WatchService(s string) WatchOption { } } +// tableWatcher implements routing table Watcher type tableWatcher struct { sync.RWMutex id string @@ -113,8 +120,3 @@ func (w *tableWatcher) Stop() { close(w.done) } } - -var ( - // ErrWatcherStopped is returned when routing table watcher has been stopped - ErrWatcherStopped = errors.New("watcher stopped") -) From a4f5772555f017fdfde44c5a352c5ae42eb207eb Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 28 Aug 2019 08:41:19 +0100 Subject: [PATCH 20/93] add network field to the routes --- network/default.go | 2 + router/handler/router.go | 4 ++ router/handler/table.go | 5 ++ router/proto/router.pb.go | 101 +++++++++++++++++++++----------------- router/proto/router.proto | 6 ++- 5 files changed, 70 insertions(+), 48 deletions(-) diff --git a/network/default.go b/network/default.go index 951221ba..8a612394 100644 --- a/network/default.go +++ b/network/default.go @@ -194,6 +194,7 @@ func (n *network) process(l tunnel.Listener) { Address: event.Route.Address, Gateway: event.Route.Gateway, Network: event.Route.Network, + Router: event.Route.Router, Link: event.Route.Link, Metric: int(event.Route.Metric), } @@ -238,6 +239,7 @@ func (n *network) advertise(client transport.Client, advertChan <-chan *router.A Address: event.Route.Address, Gateway: n.options.Address, Network: event.Route.Network, + Router: event.Route.Router, Link: DefaultLink, Metric: int64(event.Route.Metric), } diff --git a/router/handler/router.go b/router/handler/router.go index a7571024..ac05f012 100644 --- a/router/handler/router.go +++ b/router/handler/router.go @@ -33,6 +33,7 @@ func (r *Router) Lookup(ctx context.Context, req *pb.LookupRequest, resp *pb.Loo Address: route.Address, Gateway: route.Gateway, Network: route.Network, + Router: route.Router, Link: route.Link, Metric: int64(route.Metric), } @@ -58,6 +59,7 @@ func (r *Router) Advertise(ctx context.Context, req *pb.Request, stream pb.Route Address: event.Route.Address, Gateway: event.Route.Gateway, Network: event.Route.Network, + Router: event.Route.Router, Link: event.Route.Link, Metric: int64(event.Route.Metric), } @@ -97,6 +99,7 @@ func (r *Router) Process(ctx context.Context, req *pb.Advert, rsp *pb.ProcessRes Address: event.Route.Address, Gateway: event.Route.Gateway, Network: event.Route.Network, + Router: event.Route.Router, Link: event.Route.Link, Metric: int(event.Route.Metric), } @@ -161,6 +164,7 @@ func (r *Router) Watch(ctx context.Context, req *pb.WatchRequest, stream pb.Rout Address: event.Route.Address, Gateway: event.Route.Gateway, Network: event.Route.Network, + Router: event.Route.Router, Link: event.Route.Link, Metric: int64(event.Route.Metric), } diff --git a/router/handler/table.go b/router/handler/table.go index ad17fa51..63e3c96c 100644 --- a/router/handler/table.go +++ b/router/handler/table.go @@ -18,6 +18,7 @@ func (t *Table) Create(ctx context.Context, route *pb.Route, resp *pb.CreateResp Address: route.Address, Gateway: route.Gateway, Network: route.Network, + Router: route.Router, Link: route.Link, Metric: int(route.Metric), }) @@ -34,6 +35,7 @@ func (t *Table) Update(ctx context.Context, route *pb.Route, resp *pb.UpdateResp Address: route.Address, Gateway: route.Gateway, Network: route.Network, + Router: route.Router, Link: route.Link, Metric: int(route.Metric), }) @@ -50,6 +52,7 @@ func (t *Table) Delete(ctx context.Context, route *pb.Route, resp *pb.DeleteResp Address: route.Address, Gateway: route.Gateway, Network: route.Network, + Router: route.Router, Link: route.Link, Metric: int(route.Metric), }) @@ -74,6 +77,7 @@ func (t *Table) List(ctx context.Context, req *pb.Request, resp *pb.ListResponse Address: route.Address, Gateway: route.Gateway, Network: route.Network, + Router: route.Router, Link: route.Link, Metric: int64(route.Metric), } @@ -102,6 +106,7 @@ func (t *Table) Query(ctx context.Context, req *pb.QueryRequest, resp *pb.QueryR Address: route.Address, Gateway: route.Gateway, Network: route.Network, + Router: route.Router, Link: route.Link, Metric: int64(route.Metric), } diff --git a/router/proto/router.pb.go b/router/proto/router.pb.go index c448e484..6dedf13d 100644 --- a/router/proto/router.pb.go +++ b/router/proto/router.pb.go @@ -672,10 +672,12 @@ type Route struct { Gateway string `protobuf:"bytes,3,opt,name=gateway,proto3" json:"gateway,omitempty"` // the network for this destination Network string `protobuf:"bytes,4,opt,name=network,proto3" json:"network,omitempty"` + // router if the router id + Router string `protobuf:"bytes,5,opt,name=router,proto3" json:"router,omitempty"` // the network link - Link string `protobuf:"bytes,5,opt,name=link,proto3" json:"link,omitempty"` + Link string `protobuf:"bytes,6,opt,name=link,proto3" json:"link,omitempty"` // the metric / score of this route - Metric int64 `protobuf:"varint,6,opt,name=metric,proto3" json:"metric,omitempty"` + Metric int64 `protobuf:"varint,7,opt,name=metric,proto3" json:"metric,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -734,6 +736,13 @@ func (m *Route) GetNetwork() string { return "" } +func (m *Route) GetRouter() string { + if m != nil { + return m.Router + } + return "" +} + func (m *Route) GetLink() string { if m != nil { return m.Link @@ -861,51 +870,51 @@ func init() { } var fileDescriptor_6a36eee0b1adf739 = []byte{ - // 689 bytes of a gzipped FileDescriptorProto + // 699 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0xcd, 0x4e, 0xdb, 0x40, - 0x10, 0xb6, 0x93, 0xd8, 0x28, 0xd3, 0x10, 0xdc, 0x51, 0x05, 0x56, 0x5a, 0x20, 0xf2, 0x29, 0x42, - 0xd4, 0xa9, 0xd2, 0x6b, 0xff, 0x02, 0xa5, 0xaa, 0x54, 0x0e, 0xad, 0x0b, 0xea, 0xd9, 0xd8, 0x23, - 0x6a, 0x91, 0xd8, 0x66, 0x77, 0x03, 0xca, 0xb9, 0x8f, 0xd1, 0x27, 0xe8, 0x73, 0xf5, 0xda, 0x87, - 0xa8, 0xbc, 0xbb, 0x0e, 0x21, 0xc6, 0x48, 0x70, 0xf2, 0xce, 0xdf, 0x37, 0xff, 0x63, 0x18, 0x4c, - 0x93, 0x88, 0x65, 0xc3, 0xf3, 0xec, 0xa5, 0x7a, 0xb0, 0x6c, 0x26, 0x88, 0x0d, 0x73, 0x96, 0x89, - 0x92, 0xf0, 0x25, 0x81, 0x1b, 0xe7, 0x99, 0x2f, 0x75, 0x7c, 0xc5, 0xf6, 0xda, 0xb0, 0x16, 0xd0, - 0xe5, 0x8c, 0xb8, 0xf0, 0xde, 0x41, 0xe7, 0x38, 0xe1, 0x22, 0x20, 0x9e, 0x67, 0x29, 0x27, 0xf4, - 0xc1, 0x96, 0x4a, 0xdc, 0x35, 0xfb, 0xcd, 0xc1, 0x93, 0xd1, 0xa6, 0xbf, 0x62, 0xec, 0x07, 0xc5, - 0x27, 0xd0, 0x5a, 0xde, 0x5b, 0x58, 0x3f, 0xce, 0xb2, 0x8b, 0x59, 0xae, 0x01, 0x71, 0x1f, 0xac, - 0xcb, 0x19, 0xb1, 0xb9, 0x6b, 0xf6, 0xcd, 0x3b, 0xed, 0xbf, 0x15, 0xd2, 0x40, 0x29, 0x79, 0x1f, - 0xa0, 0x5b, 0x9a, 0x3f, 0x32, 0x80, 0x37, 0xd0, 0x51, 0x88, 0x8f, 0xf2, 0xff, 0x1e, 0xd6, 0xb5, - 0xf5, 0x23, 0xdd, 0x77, 0xa1, 0xf3, 0x23, 0x14, 0xd1, 0xcf, 0xb2, 0x9e, 0x7f, 0x4c, 0xb0, 0xc7, - 0xf1, 0x15, 0x31, 0x81, 0x5d, 0x68, 0x24, 0xb1, 0x0c, 0xa3, 0x1d, 0x34, 0x92, 0x18, 0x87, 0xd0, - 0x12, 0xf3, 0x9c, 0xdc, 0x46, 0xdf, 0x1c, 0x74, 0x47, 0xcf, 0x2b, 0xc0, 0xca, 0xec, 0x64, 0x9e, - 0x53, 0x20, 0x15, 0xf1, 0x05, 0xb4, 0x45, 0x32, 0x25, 0x2e, 0xc2, 0x69, 0xee, 0x36, 0xfb, 0xe6, - 0xa0, 0x19, 0xdc, 0x30, 0xd0, 0x81, 0xa6, 0x10, 0x13, 0xb7, 0x25, 0xf9, 0xc5, 0xb3, 0x88, 0x9d, - 0xae, 0x28, 0x15, 0xdc, 0xb5, 0x6a, 0x62, 0x3f, 0x2a, 0xc4, 0x81, 0xd6, 0xf2, 0x9e, 0xc2, 0xc6, - 0x57, 0x96, 0x45, 0xc4, 0x79, 0x99, 0xbe, 0xe7, 0x40, 0xf7, 0x90, 0x51, 0x28, 0x68, 0x99, 0xf3, - 0x91, 0x26, 0x74, 0x9b, 0x73, 0x9a, 0xc7, 0xcb, 0x3a, 0xbf, 0x4c, 0xb0, 0x24, 0x34, 0xfa, 0x3a, - 0x47, 0x53, 0xe6, 0xd8, 0xbb, 0x3b, 0x80, 0xba, 0x14, 0x1b, 0xab, 0x29, 0xee, 0x83, 0x25, 0xed, - 0x64, 0xf2, 0xf5, 0xbd, 0x50, 0x4a, 0xde, 0x29, 0x58, 0xb2, 0x97, 0xe8, 0xc2, 0x1a, 0x27, 0x76, - 0x95, 0x44, 0xa4, 0xab, 0x5f, 0x92, 0x85, 0xe4, 0x3c, 0x14, 0x74, 0x1d, 0xce, 0xa5, 0xb3, 0x76, - 0x50, 0x92, 0x85, 0x24, 0x25, 0x71, 0x9d, 0xb1, 0x0b, 0xe9, 0xac, 0x1d, 0x94, 0xa4, 0xf7, 0xdb, - 0x04, 0x4b, 0xfa, 0xb9, 0x1f, 0x37, 0x8c, 0x63, 0x46, 0x9c, 0x97, 0xb8, 0x9a, 0x5c, 0xf6, 0xd8, - 0xac, 0xf5, 0xd8, 0xba, 0xe5, 0x11, 0x11, 0x5a, 0x93, 0x24, 0xbd, 0x70, 0x2d, 0xc9, 0x96, 0x6f, - 0xdc, 0x04, 0x7b, 0x4a, 0x82, 0x25, 0x91, 0x6b, 0xcb, 0x2a, 0x69, 0xca, 0x1b, 0x81, 0xfd, 0x5d, - 0x84, 0x62, 0xc6, 0x0b, 0xab, 0x28, 0x8b, 0xcb, 0xd0, 0xe4, 0x1b, 0x9f, 0x81, 0x45, 0x8c, 0x65, - 0x4c, 0x47, 0xa5, 0x08, 0x6f, 0x0c, 0x5d, 0x65, 0xb3, 0x98, 0xfa, 0x21, 0xd8, 0x5c, 0x72, 0xf4, - 0xd6, 0x6c, 0x55, 0x2a, 0xad, 0x0d, 0xb4, 0xda, 0xde, 0x08, 0xe0, 0x66, 0x5c, 0x11, 0xa1, 0xab, - 0xa8, 0x71, 0x9a, 0x66, 0xb3, 0x34, 0x22, 0xc7, 0x40, 0x07, 0x3a, 0x8a, 0xa7, 0x66, 0xc5, 0x31, - 0xf7, 0x86, 0xd0, 0x5e, 0xb4, 0x1f, 0x01, 0x6c, 0x35, 0x68, 0x8e, 0x51, 0xbc, 0xd5, 0x88, 0x39, - 0x66, 0xf1, 0xd6, 0x06, 0x8d, 0xd1, 0xbf, 0x06, 0xd8, 0xb2, 0xf2, 0x0c, 0xbf, 0x80, 0xad, 0xee, - 0x04, 0xee, 0x54, 0x42, 0xbb, 0x75, 0x7f, 0x7a, 0xbb, 0xb5, 0x72, 0x3d, 0xac, 0x06, 0x1e, 0x80, - 0x25, 0x77, 0x16, 0xb7, 0x2b, 0xba, 0xcb, 0xbb, 0xdc, 0xab, 0xd9, 0x1f, 0xcf, 0x78, 0x65, 0xe2, - 0x01, 0xb4, 0x55, 0x7a, 0x09, 0x27, 0x74, 0xab, 0x83, 0xa9, 0x21, 0xb6, 0x6a, 0xb6, 0x5c, 0x62, - 0x7c, 0x82, 0x35, 0xbd, 0x7f, 0x58, 0xa7, 0xd7, 0xeb, 0x57, 0x04, 0xab, 0x2b, 0x6b, 0xe0, 0xd1, - 0x62, 0x06, 0xea, 0x03, 0xd9, 0xad, 0xeb, 0xe8, 0x02, 0x66, 0xf4, 0xb7, 0x01, 0xd6, 0x49, 0x78, - 0x36, 0x21, 0x3c, 0x2c, 0x9b, 0x83, 0x35, 0x2b, 0x77, 0x07, 0xdc, 0xca, 0xd9, 0x30, 0x0a, 0x10, - 0xd5, 0xd5, 0x07, 0x80, 0xac, 0x5c, 0x1a, 0x09, 0xa2, 0xc6, 0xe1, 0x01, 0x20, 0x2b, 0xc7, 0xc9, - 0xc0, 0x31, 0xb4, 0x8a, 0x7f, 0xdc, 0x3d, 0xd5, 0xa9, 0x0e, 0xc2, 0xf2, 0x4f, 0xd1, 0x33, 0xf0, - 0x73, 0x79, 0x5b, 0xb6, 0x6b, 0xfe, 0x27, 0x1a, 0x68, 0xa7, 0x4e, 0x5c, 0x22, 0x9d, 0xd9, 0xf2, - 0x9f, 0xfc, 0xfa, 0x7f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8c, 0xd0, 0xc0, 0x27, 0xbf, 0x07, 0x00, - 0x00, + 0x10, 0xb6, 0x9d, 0xd8, 0x91, 0xa7, 0xc1, 0xb8, 0xa3, 0x0a, 0xac, 0xb4, 0x40, 0xe4, 0x53, 0x84, + 0xa8, 0x53, 0xa5, 0xd7, 0xfe, 0x05, 0x4a, 0x55, 0xa9, 0x1c, 0x5a, 0x17, 0xd4, 0xb3, 0xb1, 0x57, + 0xd4, 0x22, 0xb1, 0xcd, 0xee, 0x06, 0x94, 0x73, 0x9f, 0xa6, 0xe7, 0x3e, 0x52, 0xaf, 0x7d, 0x88, + 0xca, 0xbb, 0xeb, 0x10, 0x62, 0x8c, 0x44, 0x4e, 0xde, 0x99, 0xf9, 0xe6, 0x9b, 0x99, 0xdd, 0x99, + 0x31, 0x0c, 0xa6, 0x69, 0x4c, 0xf3, 0xe1, 0x45, 0xfe, 0x52, 0x1e, 0x68, 0x3e, 0xe3, 0x84, 0x0e, + 0x0b, 0x9a, 0xf3, 0x4a, 0x08, 0x84, 0x80, 0x9b, 0x17, 0x79, 0x20, 0x30, 0x81, 0x54, 0xfb, 0x36, + 0x74, 0x42, 0x72, 0x35, 0x23, 0x8c, 0xfb, 0xef, 0xa0, 0x7b, 0x92, 0x32, 0x1e, 0x12, 0x56, 0xe4, + 0x19, 0x23, 0x18, 0x80, 0x25, 0x40, 0xcc, 0xd3, 0xfb, 0xad, 0xc1, 0x93, 0xd1, 0x56, 0xb0, 0xe2, + 0x1c, 0x84, 0xe5, 0x27, 0x54, 0x28, 0xff, 0x2d, 0x6c, 0x9c, 0xe4, 0xf9, 0xe5, 0xac, 0x50, 0x84, + 0x78, 0x00, 0xe6, 0xd5, 0x8c, 0xd0, 0xb9, 0xa7, 0xf7, 0xf5, 0x7b, 0xfd, 0xbf, 0x95, 0xd6, 0x50, + 0x82, 0xfc, 0x0f, 0xe0, 0x54, 0xee, 0x6b, 0x26, 0xf0, 0x06, 0xba, 0x92, 0x71, 0xad, 0xf8, 0xef, + 0x61, 0x43, 0x79, 0xaf, 0x19, 0xde, 0x81, 0xee, 0x8f, 0x88, 0xc7, 0x3f, 0xab, 0xfb, 0xfc, 0xad, + 0x83, 0x35, 0x4e, 0xae, 0x09, 0xe5, 0xe8, 0x80, 0x91, 0x26, 0x22, 0x0d, 0x3b, 0x34, 0xd2, 0x04, + 0x87, 0xd0, 0xe6, 0xf3, 0x82, 0x78, 0x46, 0x5f, 0x1f, 0x38, 0xa3, 0xe7, 0x35, 0x62, 0xe9, 0x76, + 0x3a, 0x2f, 0x48, 0x28, 0x80, 0xf8, 0x02, 0x6c, 0x9e, 0x4e, 0x09, 0xe3, 0xd1, 0xb4, 0xf0, 0x5a, + 0x7d, 0x7d, 0xd0, 0x0a, 0x6f, 0x15, 0xe8, 0x42, 0x8b, 0xf3, 0x89, 0xd7, 0x16, 0xfa, 0xf2, 0x58, + 0xe6, 0x4e, 0xae, 0x49, 0xc6, 0x99, 0x67, 0x36, 0xe4, 0x7e, 0x5c, 0x9a, 0x43, 0x85, 0xf2, 0x9f, + 0xc2, 0xe6, 0x57, 0x9a, 0xc7, 0x84, 0xb1, 0xaa, 0x7c, 0xdf, 0x05, 0xe7, 0x88, 0x92, 0x88, 0x93, + 0x65, 0xcd, 0x47, 0x32, 0x21, 0x77, 0x35, 0x67, 0x45, 0xb2, 0x8c, 0xf9, 0xa5, 0x83, 0x29, 0xa8, + 0x31, 0x50, 0x35, 0xea, 0xa2, 0xc6, 0xde, 0xfd, 0x09, 0x34, 0x95, 0x68, 0xac, 0x96, 0x78, 0x00, + 0xa6, 0xf0, 0x13, 0xc5, 0x37, 0xbf, 0x85, 0x04, 0xf9, 0x67, 0x60, 0x8a, 0xb7, 0x44, 0x0f, 0x3a, + 0x8c, 0xd0, 0xeb, 0x34, 0x26, 0xea, 0xf6, 0x2b, 0xb1, 0xb4, 0x5c, 0x44, 0x9c, 0xdc, 0x44, 0x73, + 0x11, 0xcc, 0x0e, 0x2b, 0xb1, 0xb4, 0x64, 0x84, 0xdf, 0xe4, 0xf4, 0x52, 0x04, 0xb3, 0xc3, 0x4a, + 0xf4, 0xff, 0xe8, 0x60, 0x8a, 0x38, 0x0f, 0xf3, 0x46, 0x49, 0x42, 0x09, 0x63, 0x15, 0xaf, 0x12, + 0x97, 0x23, 0xb6, 0x1a, 0x23, 0xb6, 0xef, 0x44, 0xc4, 0x2d, 0xd5, 0x83, 0xd4, 0x33, 0x85, 0x41, + 0x49, 0x88, 0xd0, 0x9e, 0xa4, 0xd9, 0xa5, 0x67, 0x09, 0xad, 0x38, 0x97, 0xd8, 0x29, 0xe1, 0x34, + 0x8d, 0xbd, 0x8e, 0xb8, 0x3d, 0x25, 0xf9, 0x23, 0xb0, 0xbe, 0xf3, 0x88, 0xcf, 0x58, 0xe9, 0x15, + 0xe7, 0x49, 0x95, 0xb2, 0x38, 0xe3, 0x33, 0x30, 0x09, 0xa5, 0x39, 0x55, 0xd9, 0x4a, 0xc1, 0x1f, + 0x83, 0x23, 0x7d, 0x16, 0xd3, 0x30, 0x04, 0x8b, 0x09, 0x8d, 0x9a, 0xa6, 0xed, 0xda, 0x0b, 0x28, + 0x07, 0x05, 0xdb, 0x1f, 0x01, 0xdc, 0xb6, 0x31, 0x22, 0x38, 0x52, 0x1a, 0x67, 0x59, 0x3e, 0xcb, + 0x62, 0xe2, 0x6a, 0xe8, 0x42, 0x57, 0xea, 0x64, 0x0f, 0xb9, 0xfa, 0xfe, 0x10, 0xec, 0x45, 0x5b, + 0x20, 0x80, 0x25, 0x1b, 0xd0, 0xd5, 0xca, 0xb3, 0x6c, 0x3d, 0x57, 0x2f, 0xcf, 0xca, 0xc1, 0x18, + 0xfd, 0x33, 0xc0, 0x0a, 0xe5, 0x95, 0x7c, 0x01, 0x4b, 0xee, 0x0f, 0xdc, 0xad, 0xa5, 0x76, 0x67, + 0x2f, 0xf5, 0xf6, 0x1a, 0xed, 0xaa, 0x89, 0x35, 0x3c, 0x04, 0x53, 0xcc, 0x32, 0xee, 0xd4, 0xb0, + 0xcb, 0x33, 0xde, 0x6b, 0x98, 0x2b, 0x5f, 0x7b, 0xa5, 0xe3, 0x21, 0xd8, 0xb2, 0xbc, 0x94, 0x11, + 0xf4, 0xea, 0x0d, 0xab, 0x28, 0xb6, 0x1b, 0xa6, 0x5f, 0x70, 0x7c, 0x82, 0x8e, 0x9a, 0x4b, 0x6c, + 0xc2, 0xf5, 0xfa, 0x35, 0xc3, 0xea, 0x28, 0x6b, 0x78, 0xbc, 0xe8, 0x81, 0xe6, 0x44, 0xf6, 0x9a, + 0x5e, 0x74, 0x41, 0x33, 0xfa, 0x6b, 0x80, 0x79, 0x1a, 0x9d, 0x4f, 0x08, 0x1e, 0x55, 0x8f, 0x83, + 0x0d, 0xa3, 0x78, 0x0f, 0xdd, 0xca, 0x3a, 0xd1, 0x4a, 0x12, 0xf9, 0xaa, 0x8f, 0x20, 0x59, 0xd9, + 0x40, 0x82, 0x44, 0xb6, 0xc3, 0x23, 0x48, 0x56, 0x96, 0x96, 0x86, 0x63, 0x68, 0x97, 0xff, 0xbe, + 0x07, 0x6e, 0xa7, 0xde, 0x08, 0xcb, 0x3f, 0x4b, 0x5f, 0xc3, 0xcf, 0xd5, 0xce, 0xd9, 0x69, 0xf8, + 0xcf, 0x28, 0xa2, 0xdd, 0x26, 0x73, 0xc5, 0x74, 0x6e, 0x89, 0x7f, 0xf5, 0xeb, 0xff, 0x01, 0x00, + 0x00, 0xff, 0xff, 0xe2, 0xe9, 0xe2, 0x3b, 0xd7, 0x07, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. diff --git a/router/proto/router.proto b/router/proto/router.proto index 3e18d456..543fdd97 100644 --- a/router/proto/router.proto +++ b/router/proto/router.proto @@ -117,10 +117,12 @@ message Route { string gateway = 3; // the network for this destination string network = 4; + // router if the router id + string router = 5; // the network link - string link = 5; + string link = 6; // the metric / score of this route - int64 metric = 6; + int64 metric = 7; } message Status { From d09b7dbbef0da3ef4713c6b573d87c0dbe4f9628 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Wed, 28 Aug 2019 20:11:19 +0100 Subject: [PATCH 21/93] Broadcast neighbourhood; fix critical bugs in channel connections This commit introduces neighbourhood announcements which allows to maintaing neighbour map if each next-hop node. It also fixes a critical bug when accepting connections for a particular tunnel channel. --- network/default.go | 371 +++++++++++++++++++++++++++++---- network/network.go | 4 +- network/proto/network.micro.go | 21 ++ network/proto/network.pb.go | 227 ++++++++++++++++++++ network/proto/network.proto | 31 +++ 5 files changed, 607 insertions(+), 47 deletions(-) create mode 100644 network/proto/network.micro.go create mode 100644 network/proto/network.pb.go create mode 100644 network/proto/network.proto diff --git a/network/default.go b/network/default.go index 8a612394..c903d4b2 100644 --- a/network/default.go +++ b/network/default.go @@ -7,9 +7,10 @@ import ( "github.com/golang/protobuf/proto" "github.com/micro/go-micro/client" rtr "github.com/micro/go-micro/client/selector/router" + pbNet "github.com/micro/go-micro/network/proto" "github.com/micro/go-micro/proxy" "github.com/micro/go-micro/router" - pb "github.com/micro/go-micro/router/proto" + pbRtr "github.com/micro/go-micro/router/proto" "github.com/micro/go-micro/server" "github.com/micro/go-micro/transport" "github.com/micro/go-micro/tunnel" @@ -18,12 +19,22 @@ import ( ) var ( - // ControlChannel is the name of the tunnel channel for passing contron message + // NetworkChannel is the name of the tunnel channel for passing network messages + NetworkChannel = "network" + // ControlChannel is the name of the tunnel channel for passing control message ControlChannel = "control" // DefaultLink is default network link DefaultLink = "network" ) +// node is network node +type node struct { + // Id is node id + Id string + // Address is node address + Address string +} + // network implements Network interface type network struct { // options configure the network @@ -39,11 +50,18 @@ type network struct { // client is network client client client.Client + // ctrlClient is ControlChannel client + ctrlClient transport.Client + // netClient is NetwrokChannel client + netClient transport.Client + sync.RWMutex // connected marks the network as connected connected bool // closed closes the network closed chan bool + // neighbours maps the node neighbourhood + neighbours map[node]map[node]bool } // newNetwork returns a new network node @@ -88,12 +106,13 @@ func newNetwork(opts ...Option) Network { ) return &network{ - options: options, - Router: options.Router, - Proxy: options.Proxy, - Tunnel: options.Tunnel, - server: server, - client: client, + options: options, + Router: options.Router, + Proxy: options.Proxy, + Tunnel: options.Tunnel, + server: server, + client: client, + neighbours: make(map[node]map[node]bool), } } @@ -107,6 +126,7 @@ func (n *network) Address() string { return n.Tunnel.Address() } +// resolveNodes resolves network nodes to addresses func (n *network) resolveNodes() ([]string, error) { // resolve the network address to network nodes records, err := n.options.Resolver.Resolve(n.options.Name) @@ -123,6 +143,7 @@ func (n *network) resolveNodes() ([]string, error) { return nodes, nil } +// resolve continuously resolves network nodes and initializes network tunnel with resolved addresses func (n *network) resolve() { resolve := time.NewTicker(ResolveTime) defer resolve.Stop() @@ -145,7 +166,180 @@ func (n *network) resolve() { } } -func (n *network) handleConn(conn tunnel.Conn, msg chan *transport.Message) { +// handleNetConn handles network announcement messages +func (n *network) handleNetConn(conn tunnel.Conn, msg chan *transport.Message) { + for { + m := new(transport.Message) + if err := conn.Recv(m); err != nil { + // TODO: should we bail here? + log.Debugf("Network tunnel [%s] receive error: %v", NetworkChannel, err) + return + } + + select { + case msg <- m: + case <-n.closed: + return + } + } +} + +// acceptNetConn accepts connections from NetworkChannel +func (n *network) acceptNetConn(l tunnel.Listener, recv chan *transport.Message) { + for { + // accept a connection + conn, err := l.Accept() + if err != nil { + // TODO: handle this + log.Debugf("Network tunnel [%s] accept error: %v", NetworkChannel, err) + return + } + + select { + case <-n.closed: + return + default: + // go handle NetworkChannel connection + go n.handleNetConn(conn, recv) + } + } +} + +// processNetChan processes messages received on NetworkChannel +func (n *network) processNetChan(l tunnel.Listener) { + // receive network message queue + recv := make(chan *transport.Message, 128) + + // accept NetworkChannel connections + go n.acceptNetConn(l, recv) + + for { + select { + case m := <-recv: + // switch on type of message and take action + switch m.Header["Micro-Method"] { + case "connect": + pbNetConnect := &pbNet.Connect{} + if err := proto.Unmarshal(m.Body, pbNetConnect); err != nil { + log.Debugf("Network tunnel [%s] connect unmarshal error: %v", NetworkChannel, err) + continue + } + // don't process your own messages + if pbNetConnect.Node.Id == n.options.Id { + continue + } + neighbour := node{ + Id: pbNetConnect.Node.Id, + Address: pbNetConnect.Node.Address, + } + n.Lock() + n.neighbours[neighbour] = make(map[node]bool) + n.Unlock() + case "neighbour": + pbNetNeighbour := &pbNet.Neighbour{} + if err := proto.Unmarshal(m.Body, pbNetNeighbour); err != nil { + log.Debugf("Network tunnel [%s] neighbour unmarshal error: %v", NetworkChannel, err) + continue + } + // don't process your own messages + if pbNetNeighbour.Node.Id == n.options.Id { + continue + } + neighbour := node{ + Id: pbNetNeighbour.Node.Id, + Address: pbNetNeighbour.Node.Address, + } + n.Lock() + // we override the existing neighbour map + n.neighbours[neighbour] = make(map[node]bool) + // store the neighbouring node and its neighbours + for _, pbNeighbour := range pbNetNeighbour.Neighbours { + neighbourNode := node{ + Id: pbNeighbour.Id, + Address: pbNeighbour.Address, + } + n.neighbours[neighbour][neighbourNode] = true + } + n.Unlock() + case "close": + pbNetClose := &pbNet.Close{} + if err := proto.Unmarshal(m.Body, pbNetClose); err != nil { + log.Debugf("Network tunnel [%s] close unmarshal error: %v", NetworkChannel, err) + continue + } + // don't process your own messages + if pbNetClose.Node.Id == n.options.Id { + continue + } + node := node{ + Id: pbNetClose.Node.Id, + Address: pbNetClose.Node.Address, + } + n.Lock() + delete(n.neighbours, node) + n.Unlock() + } + case <-n.closed: + return + } + } +} + +// announce announces node neighbourhood to the network +func (n *network) announce(client transport.Client) { + announce := time.NewTicker(AnnounceTime) + defer announce.Stop() + + for { + select { + case <-n.closed: + return + case <-announce.C: + n.RLock() + nodes := make([]*pbNet.Node, len(n.neighbours)) + i := 0 + for node, _ := range n.neighbours { + pbNode := &pbNet.Node{ + Id: node.Id, + Address: node.Address, + } + nodes[i] = pbNode + } + n.RUnlock() + + node := &pbNet.Node{ + Id: n.options.Id, + Address: n.options.Address, + } + pbNetNeighbour := &pbNet.Neighbour{ + Node: node, + Neighbours: nodes, + } + + body, err := proto.Marshal(pbNetNeighbour) + if err != nil { + // TODO: should we bail here? + log.Debugf("Network failed to marshal neighbour message: %v", err) + continue + } + // create transport message and chuck it down the pipe + m := transport.Message{ + Header: map[string]string{ + "Micro-Method": "neighbour", + }, + Body: body, + } + + if err := client.Send(&m); err != nil { + log.Debugf("Network failed to send neighbour messsage: %v", err) + continue + } + } + } +} + +// handleCtrlConn handles ControlChannel connections +func (n *network) handleCtrlConn(conn tunnel.Conn, msg chan *transport.Message) { for { m := new(transport.Message) if err := conn.Recv(m); err != nil { @@ -162,19 +356,34 @@ func (n *network) handleConn(conn tunnel.Conn, msg chan *transport.Message) { } } -func (n *network) process(l tunnel.Listener) { +// acceptCtrlConn accepts connections from ControlChannel +func (n *network) acceptCtrlConn(l tunnel.Listener, recv chan *transport.Message) { + for { + // accept a connection + conn, err := l.Accept() + if err != nil { + // TODO: handle this + log.Debugf("Network tunnel [%s] accept error: %v", ControlChannel, err) + return + } + + select { + case <-n.closed: + return + default: + // go handle ControlChannel connection + go n.handleCtrlConn(conn, recv) + } + } +} + +// process processes network advertisements +func (n *network) processCtrlChan(l tunnel.Listener) { // receive control message queue recv := make(chan *transport.Message, 128) - // accept a connection - conn, err := l.Accept() - if err != nil { - // TODO: handle this - log.Debugf("Network tunnel accept error: %v", err) - return - } - - go n.handleConn(conn, recv) + // accept ControlChannel cconnections + go n.acceptCtrlConn(l, recv) for { select { @@ -182,13 +391,13 @@ func (n *network) process(l tunnel.Listener) { // switch on type of message and take action switch m.Header["Micro-Method"] { case "advert": - pbAdvert := &pb.Advert{} - if err := proto.Unmarshal(m.Body, pbAdvert); err != nil { + pbRtrAdvert := &pbRtr.Advert{} + if err := proto.Unmarshal(m.Body, pbRtrAdvert); err != nil { continue } var events []*router.Event - for _, event := range pbAdvert.Events { + for _, event := range pbRtrAdvert.Events { route := router.Route{ Service: event.Route.Service, Address: event.Route.Address, @@ -200,16 +409,16 @@ func (n *network) process(l tunnel.Listener) { } e := &router.Event{ Type: router.EventType(event.Type), - Timestamp: time.Unix(0, pbAdvert.Timestamp), + Timestamp: time.Unix(0, pbRtrAdvert.Timestamp), Route: route, } events = append(events, e) } advert := &router.Advert{ - Id: pbAdvert.Id, - Type: router.AdvertType(pbAdvert.Type), - Timestamp: time.Unix(0, pbAdvert.Timestamp), - TTL: time.Duration(pbAdvert.Ttl), + Id: pbRtrAdvert.Id, + Type: router.AdvertType(pbRtrAdvert.Type), + Timestamp: time.Unix(0, pbRtrAdvert.Timestamp), + TTL: time.Duration(pbRtrAdvert.Ttl), Events: events, } @@ -231,10 +440,10 @@ func (n *network) advertise(client transport.Client, advertChan <-chan *router.A // process local adverts and randomly fire them at other nodes case advert := <-advertChan: // create a proto advert - var events []*pb.Event + var events []*pbRtr.Event for _, event := range advert.Events { // NOTE: we override the Gateway and Link fields here - route := &pb.Route{ + route := &pbRtr.Route{ Service: event.Route.Service, Address: event.Route.Address, Gateway: n.options.Address, @@ -243,23 +452,23 @@ func (n *network) advertise(client transport.Client, advertChan <-chan *router.A Link: DefaultLink, Metric: int64(event.Route.Metric), } - e := &pb.Event{ - Type: pb.EventType(event.Type), + e := &pbRtr.Event{ + Type: pbRtr.EventType(event.Type), Timestamp: event.Timestamp.UnixNano(), Route: route, } events = append(events, e) } - pbAdvert := &pb.Advert{ + pbRtrAdvert := &pbRtr.Advert{ Id: advert.Id, - Type: pb.AdvertType(advert.Type), + Type: pbRtr.AdvertType(advert.Type), Timestamp: advert.Timestamp.UnixNano(), Events: events, } - body, err := proto.Marshal(pbAdvert) + body, err := proto.Marshal(pbRtrAdvert) if err != nil { // TODO: should we bail here? - log.Debugf("Network failed to marshal message: %v", err) + log.Debugf("Network failed to marshal advert message: %v", err) continue } // create transport message and chuck it down the pipe @@ -271,7 +480,7 @@ func (n *network) advertise(client transport.Client, advertChan <-chan *router.A } if err := client.Send(&m); err != nil { - log.Debugf("Network failed to send advert %s: %v", pbAdvert.Id, err) + log.Debugf("Network failed to send advert %s: %v", pbRtrAdvert.Id, err) continue } case <-n.closed: @@ -307,13 +516,29 @@ func (n *network) Connect() error { ) // dial into ControlChannel to send route adverts - client, err := n.Tunnel.Dial(ControlChannel) + ctrlClient, err := n.Tunnel.Dial(ControlChannel) if err != nil { return err } + n.ctrlClient = ctrlClient + // listen on ControlChannel - listener, err := n.Tunnel.Listen(ControlChannel) + ctrlListener, err := n.Tunnel.Listen(ControlChannel) + if err != nil { + return err + } + + // dial into NetworkChannel to send network messages + netClient, err := n.Tunnel.Dial(NetworkChannel) + if err != nil { + return err + } + + n.netClient = netClient + + // listen on NetworkChannel + netListener, err := n.Tunnel.Listen(NetworkChannel) if err != nil { return err } @@ -321,9 +546,6 @@ func (n *network) Connect() error { // create closed channel n.closed = make(chan bool) - // keep resolving network nodes - go n.resolve() - // start the router if err := n.options.Router.Start(); err != nil { return err @@ -335,19 +557,48 @@ func (n *network) Connect() error { return err } - // advertise routes - go n.advertise(client, advertChan) - // accept and process routes - go n.process(listener) - // start the server if err := n.server.Start(); err != nil { return err } + // go resolving network nodes + go n.resolve() + // broadcast neighbourhood + go n.announce(netClient) + // listen to network messages + go n.processNetChan(netListener) + // advertise service routes + go n.advertise(ctrlClient, advertChan) + // accept and process routes + go n.processCtrlChan(ctrlListener) + // set connected to true n.connected = true + // send connect message to NetworkChannel + node := &pbNet.Node{ + Id: n.options.Id, + Address: n.options.Address, + } + pbNetConnect := &pbNet.Connect{ + Node: node, + } + + // only proceed with sending to NetworkChannel if marshal succeeds + if body, err := proto.Marshal(pbNetConnect); err == nil { + m := transport.Message{ + Header: map[string]string{ + "Micro-Method": "connect", + }, + Body: body, + } + + if err := netClient.Send(&m); err != nil { + log.Debugf("Network failed to send connect messsage: %v", err) + } + } + return nil } @@ -383,11 +634,39 @@ func (n *network) Close() error { case <-n.closed: return nil default: + // TODO: send close message to the network channel close(n.closed) // set connected to false n.connected = false } + // send close message only if we managed to connect to NetworkChannel + if n.netClient != nil { + // send connect message to NetworkChannel + node := &pbNet.Node{ + Id: n.options.Id, + Address: n.options.Address, + } + pbNetClose := &pbNet.Close{ + Node: node, + } + + // only proceed with sending to NetworkChannel if marshal succeeds + if body, err := proto.Marshal(pbNetClose); err == nil { + // create transport message and chuck it down the pipe + m := transport.Message{ + Header: map[string]string{ + "Micro-Method": "close", + }, + Body: body, + } + + if err := n.netClient.Send(&m); err != nil { + log.Debugf("Network failed to send close messsage: %v", err) + } + } + } + return n.close() } diff --git a/network/network.go b/network/network.go index 46801e0c..d9d10a55 100644 --- a/network/network.go +++ b/network/network.go @@ -13,8 +13,10 @@ var ( DefaultName = "go.micro" // DefaultAddress is default network address DefaultAddress = ":0" - // ResolveTime ddefines the time to periodically resolve network nodes + // ResolveTime defines time interval to periodically resolve network nodes ResolveTime = 1 * time.Minute + // AnnounceTime defines time interval to periodically announce node neighbours + AnnounceTime = 30 * time.Second ) // Network is micro network diff --git a/network/proto/network.micro.go b/network/proto/network.micro.go new file mode 100644 index 00000000..814e6dba --- /dev/null +++ b/network/proto/network.micro.go @@ -0,0 +1,21 @@ +// Code generated by protoc-gen-micro. DO NOT EDIT. +// source: network.proto + +package go_micro_network + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package diff --git a/network/proto/network.pb.go b/network/proto/network.pb.go new file mode 100644 index 00000000..1a1d5948 --- /dev/null +++ b/network/proto/network.pb.go @@ -0,0 +1,227 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: network.proto + +package go_micro_network + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +// Node is network node +type Node struct { + // node ide + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // node address + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Node) Reset() { *m = Node{} } +func (m *Node) String() string { return proto.CompactTextString(m) } +func (*Node) ProtoMessage() {} +func (*Node) Descriptor() ([]byte, []int) { + return fileDescriptor_8571034d60397816, []int{0} +} + +func (m *Node) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Node.Unmarshal(m, b) +} +func (m *Node) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Node.Marshal(b, m, deterministic) +} +func (m *Node) XXX_Merge(src proto.Message) { + xxx_messageInfo_Node.Merge(m, src) +} +func (m *Node) XXX_Size() int { + return xxx_messageInfo_Node.Size(m) +} +func (m *Node) XXX_DiscardUnknown() { + xxx_messageInfo_Node.DiscardUnknown(m) +} + +var xxx_messageInfo_Node proto.InternalMessageInfo + +func (m *Node) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *Node) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +// Connect is sent when the node connects to the network +type Connect struct { + // network mode + Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Connect) Reset() { *m = Connect{} } +func (m *Connect) String() string { return proto.CompactTextString(m) } +func (*Connect) ProtoMessage() {} +func (*Connect) Descriptor() ([]byte, []int) { + return fileDescriptor_8571034d60397816, []int{1} +} + +func (m *Connect) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Connect.Unmarshal(m, b) +} +func (m *Connect) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Connect.Marshal(b, m, deterministic) +} +func (m *Connect) XXX_Merge(src proto.Message) { + xxx_messageInfo_Connect.Merge(m, src) +} +func (m *Connect) XXX_Size() int { + return xxx_messageInfo_Connect.Size(m) +} +func (m *Connect) XXX_DiscardUnknown() { + xxx_messageInfo_Connect.DiscardUnknown(m) +} + +var xxx_messageInfo_Connect proto.InternalMessageInfo + +func (m *Connect) GetNode() *Node { + if m != nil { + return m.Node + } + return nil +} + +// Close is sent when the node disconnects from the network +type Close struct { + // network mode + Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Close) Reset() { *m = Close{} } +func (m *Close) String() string { return proto.CompactTextString(m) } +func (*Close) ProtoMessage() {} +func (*Close) Descriptor() ([]byte, []int) { + return fileDescriptor_8571034d60397816, []int{2} +} + +func (m *Close) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Close.Unmarshal(m, b) +} +func (m *Close) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Close.Marshal(b, m, deterministic) +} +func (m *Close) XXX_Merge(src proto.Message) { + xxx_messageInfo_Close.Merge(m, src) +} +func (m *Close) XXX_Size() int { + return xxx_messageInfo_Close.Size(m) +} +func (m *Close) XXX_DiscardUnknown() { + xxx_messageInfo_Close.DiscardUnknown(m) +} + +var xxx_messageInfo_Close proto.InternalMessageInfo + +func (m *Close) GetNode() *Node { + if m != nil { + return m.Node + } + return nil +} + +// Neighbour is used to nnounce node neighbourhood +type Neighbour struct { + // network mode + Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` + // neighbours + Neighbours []*Node `protobuf:"bytes,3,rep,name=neighbours,proto3" json:"neighbours,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Neighbour) Reset() { *m = Neighbour{} } +func (m *Neighbour) String() string { return proto.CompactTextString(m) } +func (*Neighbour) ProtoMessage() {} +func (*Neighbour) Descriptor() ([]byte, []int) { + return fileDescriptor_8571034d60397816, []int{3} +} + +func (m *Neighbour) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Neighbour.Unmarshal(m, b) +} +func (m *Neighbour) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Neighbour.Marshal(b, m, deterministic) +} +func (m *Neighbour) XXX_Merge(src proto.Message) { + xxx_messageInfo_Neighbour.Merge(m, src) +} +func (m *Neighbour) XXX_Size() int { + return xxx_messageInfo_Neighbour.Size(m) +} +func (m *Neighbour) XXX_DiscardUnknown() { + xxx_messageInfo_Neighbour.DiscardUnknown(m) +} + +var xxx_messageInfo_Neighbour proto.InternalMessageInfo + +func (m *Neighbour) GetNode() *Node { + if m != nil { + return m.Node + } + return nil +} + +func (m *Neighbour) GetNeighbours() []*Node { + if m != nil { + return m.Neighbours + } + return nil +} + +func init() { + proto.RegisterType((*Node)(nil), "go.micro.network.Node") + proto.RegisterType((*Connect)(nil), "go.micro.network.Connect") + proto.RegisterType((*Close)(nil), "go.micro.network.Close") + proto.RegisterType((*Neighbour)(nil), "go.micro.network.Neighbour") +} + +func init() { proto.RegisterFile("network.proto", fileDescriptor_8571034d60397816) } + +var fileDescriptor_8571034d60397816 = []byte{ + // 173 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcd, 0x4b, 0x2d, 0x29, + 0xcf, 0x2f, 0xca, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x48, 0xcf, 0xd7, 0xcb, 0xcd, + 0x4c, 0x2e, 0xca, 0xd7, 0x83, 0x8a, 0x2b, 0x19, 0x70, 0xb1, 0xf8, 0xe5, 0xa7, 0xa4, 0x0a, 0xf1, + 0x71, 0x31, 0x65, 0xa6, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0x31, 0x65, 0xa6, 0x08, 0x49, + 0x70, 0xb1, 0x27, 0xa6, 0xa4, 0x14, 0xa5, 0x16, 0x17, 0x4b, 0x30, 0x81, 0x05, 0x61, 0x5c, 0x25, + 0x53, 0x2e, 0x76, 0xe7, 0xfc, 0xbc, 0xbc, 0xd4, 0xe4, 0x12, 0x21, 0x2d, 0x2e, 0x96, 0xbc, 0xfc, + 0x94, 0x54, 0xb0, 0x36, 0x6e, 0x23, 0x31, 0x3d, 0x74, 0xd3, 0xf5, 0x40, 0x46, 0x07, 0x81, 0xd5, + 0x28, 0x19, 0x73, 0xb1, 0x3a, 0xe7, 0xe4, 0x17, 0xa7, 0x92, 0xa4, 0x29, 0x9f, 0x8b, 0xd3, 0x2f, + 0x35, 0x33, 0x3d, 0x23, 0x29, 0xbf, 0xb4, 0x88, 0x14, 0x8d, 0x42, 0x66, 0x5c, 0x5c, 0x79, 0x30, + 0x8d, 0xc5, 0x12, 0xcc, 0x0a, 0xcc, 0x78, 0x74, 0x20, 0xa9, 0x4c, 0x62, 0x03, 0x87, 0x93, 0x31, + 0x20, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x74, 0x00, 0x71, 0x38, 0x01, 0x00, 0x00, +} diff --git a/network/proto/network.proto b/network/proto/network.proto new file mode 100644 index 00000000..10483116 --- /dev/null +++ b/network/proto/network.proto @@ -0,0 +1,31 @@ +syntax = "proto3"; + +package go.micro.network; + +// Node is network node +message Node { + // node ide + string id = 1; + // node address + string address = 2; +} + +// Connect is sent when the node connects to the network +message Connect { + // network mode + Node node = 1; +} + +// Close is sent when the node disconnects from the network +message Close { + // network mode + Node node = 1; +} + +// Neighbour is used to nnounce node neighbourhood +message Neighbour { + // network mode + Node node = 1; + // neighbours + repeated Node neighbours = 3; +} From db8e2620cbfd6ae9a9506b3f247256ffed258534 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Wed, 28 Aug 2019 23:11:26 +0100 Subject: [PATCH 22/93] Make tunnel channel clients key-able. Neighbour map simplified. tunClient is a map of tunnel clients keyed on tunnel channel name. Neighbour map is now a cimple map of nodes which contains its nodes. --- network/default.go | 67 +++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/network/default.go b/network/default.go index c903d4b2..3f917787 100644 --- a/network/default.go +++ b/network/default.go @@ -29,10 +29,12 @@ var ( // node is network node type node struct { - // Id is node id - Id string - // Address is node address - Address string + // id is node id + id string + // address is node address + address string + // neighbours are node neightbours + neighbours map[string]*node } // network implements Network interface @@ -50,10 +52,8 @@ type network struct { // client is network client client client.Client - // ctrlClient is ControlChannel client - ctrlClient transport.Client - // netClient is NetwrokChannel client - netClient transport.Client + // tunClient is a mao of tunnel clients keyed over channel names + tunClient map[string]transport.Client sync.RWMutex // connected marks the network as connected @@ -61,7 +61,7 @@ type network struct { // closed closes the network closed chan bool // neighbours maps the node neighbourhood - neighbours map[node]map[node]bool + neighbours map[string]*node } // newNetwork returns a new network node @@ -112,7 +112,8 @@ func newNetwork(opts ...Option) Network { Tunnel: options.Tunnel, server: server, client: client, - neighbours: make(map[node]map[node]bool), + tunClient: make(map[string]transport.Client), + neighbours: make(map[string]*node), } } @@ -228,12 +229,13 @@ func (n *network) processNetChan(l tunnel.Listener) { if pbNetConnect.Node.Id == n.options.Id { continue } - neighbour := node{ - Id: pbNetConnect.Node.Id, - Address: pbNetConnect.Node.Address, + neighbour := &node{ + id: pbNetConnect.Node.Id, + address: pbNetConnect.Node.Address, + neighbours: make(map[string]*node), } n.Lock() - n.neighbours[neighbour] = make(map[node]bool) + n.neighbours[neighbour.id] = neighbour n.Unlock() case "neighbour": pbNetNeighbour := &pbNet.Neighbour{} @@ -245,20 +247,21 @@ func (n *network) processNetChan(l tunnel.Listener) { if pbNetNeighbour.Node.Id == n.options.Id { continue } - neighbour := node{ - Id: pbNetNeighbour.Node.Id, - Address: pbNetNeighbour.Node.Address, + neighbour := &node{ + id: pbNetNeighbour.Node.Id, + address: pbNetNeighbour.Node.Address, + neighbours: make(map[string]*node), } n.Lock() // we override the existing neighbour map - n.neighbours[neighbour] = make(map[node]bool) + n.neighbours[neighbour.id] = neighbour // store the neighbouring node and its neighbours for _, pbNeighbour := range pbNetNeighbour.Neighbours { - neighbourNode := node{ - Id: pbNeighbour.Id, - Address: pbNeighbour.Address, + neighbourNode := &node{ + id: pbNeighbour.Id, + address: pbNeighbour.Address, } - n.neighbours[neighbour][neighbourNode] = true + n.neighbours[neighbour.id].neighbours[neighbourNode.id] = neighbourNode } n.Unlock() case "close": @@ -271,12 +274,8 @@ func (n *network) processNetChan(l tunnel.Listener) { if pbNetClose.Node.Id == n.options.Id { continue } - node := node{ - Id: pbNetClose.Node.Id, - Address: pbNetClose.Node.Address, - } n.Lock() - delete(n.neighbours, node) + delete(n.neighbours, pbNetClose.Node.Id) n.Unlock() } case <-n.closed: @@ -298,10 +297,10 @@ func (n *network) announce(client transport.Client) { n.RLock() nodes := make([]*pbNet.Node, len(n.neighbours)) i := 0 - for node, _ := range n.neighbours { + for id, _ := range n.neighbours { pbNode := &pbNet.Node{ - Id: node.Id, - Address: node.Address, + Id: id, + Address: n.neighbours[id].address, } nodes[i] = pbNode } @@ -521,7 +520,7 @@ func (n *network) Connect() error { return err } - n.ctrlClient = ctrlClient + n.tunClient[ControlChannel] = ctrlClient // listen on ControlChannel ctrlListener, err := n.Tunnel.Listen(ControlChannel) @@ -535,7 +534,7 @@ func (n *network) Connect() error { return err } - n.netClient = netClient + n.tunClient[NetworkChannel] = netClient // listen on NetworkChannel netListener, err := n.Tunnel.Listen(NetworkChannel) @@ -641,7 +640,7 @@ func (n *network) Close() error { } // send close message only if we managed to connect to NetworkChannel - if n.netClient != nil { + if netClient, ok := n.tunClient[NetworkChannel]; ok { // send connect message to NetworkChannel node := &pbNet.Node{ Id: n.options.Id, @@ -661,7 +660,7 @@ func (n *network) Close() error { Body: body, } - if err := n.netClient.Send(&m); err != nil { + if err := netClient.Send(&m); err != nil { log.Debugf("Network failed to send close messsage: %v", err) } } From 6ab86c9e576139c337ed03d5b0b17e85a5e93efa Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 28 Aug 2019 23:12:22 +0100 Subject: [PATCH 23/93] Don't process unless connected, and only fire loopback messages back up the loopback --- tunnel/default.go | 106 +++++++++++++++++++++++++++++++-------------- tunnel/listener.go | 2 + tunnel/socket.go | 7 ++- 3 files changed, 82 insertions(+), 33 deletions(-) diff --git a/tunnel/default.go b/tunnel/default.go index 69a5d878..bd6af7dd 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -49,8 +49,18 @@ type tun struct { type link struct { transport.Socket - id string - loopback bool + // unique id of this link e.g uuid + // which we define for ourselves + id string + // whether its a loopback connection + loopback bool + // whether its actually connected + // dialled side sets it to connected + // after sending the message. the + // listener waits for the connect + connected bool + // the last time we received a keepalive + // on this link from the remote side lastKeepAlive time.Time } @@ -190,9 +200,25 @@ func (t *tun) process() { log.Debugf("No links to send to") } for node, link := range t.links { + // if the link is not connected skip it + if !link.connected { + log.Debugf("Link for node %s not connected", node) + continue + } + + // if the link was a loopback accepted connection + // and the message is being sent outbound via + // a dialled connection don't use this link if link.loopback && msg.outbound { continue } + + // if the message was being returned by the loopback listener + // send it back up the loopback link only + if msg.loopback && !link.loopback { + continue + } + log.Debugf("Sending %+v to %s", newMsg, node) if err := link.Send(newMsg); err != nil { log.Debugf("Tunnel error sending %+v to %s: %v", newMsg, node, err) @@ -209,15 +235,22 @@ func (t *tun) process() { // process incoming messages func (t *tun) listen(link *link) { + // remove the link on exit + defer func() { + log.Debugf("Tunnel deleting connection from %s", link.Remote()) + t.Lock() + delete(t.links, link.Remote()) + t.Unlock() + }() + + // let us know if its a loopback + var loopback bool + for { // process anything via the net interface msg := new(transport.Message) - err := link.Recv(msg) - if err != nil { + if err := link.Recv(msg); err != nil { log.Debugf("Tunnel link %s receive error: %#v", link.Remote(), err) - t.Lock() - delete(t.links, link.Remote()) - t.Unlock() return } @@ -232,11 +265,18 @@ func (t *tun) listen(link *link) { // are we connecting to ourselves? if token == t.token { - t.Lock() link.loopback = true - t.Unlock() + loopback = true } + // set as connected + link.connected = true + + // save the link once connected + t.Lock() + t.links[link.Remote()] = link + t.Unlock() + // nothing more to do continue case "close": @@ -258,6 +298,11 @@ func (t *tun) listen(link *link) { continue } + // if its not connected throw away the link + if !link.connected { + return + } + // strip message header delete(msg.Header, "Micro-Tunnel") @@ -283,8 +328,10 @@ func (t *tun) listen(link *link) { var s *socket var exists bool + // If its a loopback connection then we've enabled link direction + // listening side is used for listening, the dialling side for dialling switch { - case link.loopback: + case loopback: s, exists = t.getSocket(id, "listener") default: // get the socket based on the tunnel id and session @@ -298,6 +345,7 @@ func (t *tun) listen(link *link) { s, exists = t.getSocket(id, "listener") } } + // bail if no socket has been found if !exists { log.Debugf("Tunnel skipping no socket exists") @@ -337,9 +385,10 @@ func (t *tun) listen(link *link) { // construct the internal message imsg := &message{ - id: id, - session: session, - data: tmsg, + id: id, + session: session, + data: tmsg, + loopback: loopback, } // append to recv backlog @@ -399,13 +448,14 @@ func (t *tun) setupLink(node string) (*link, error) { return nil, err } - // save the link - id := uuid.New().String() + // create a new link link := &link{ Socket: c, - id: id, + id: uuid.New().String(), + // we made the outbound connection + // and sent the connect message + connected: true, } - t.links[node] = link // process incoming messages go t.listen(link) @@ -430,25 +480,16 @@ func (t *tun) connect() error { // accept inbound connections err := l.Accept(func(sock transport.Socket) { log.Debugf("Tunnel accepted connection from %s", sock.Remote()) - // save the link - id := uuid.New().String() - t.Lock() + + // create a new link link := &link{ Socket: sock, - id: id, + id: uuid.New().String(), } - t.links[sock.Remote()] = link - t.Unlock() - // delete the link - defer func() { - log.Debugf("Tunnel deleting connection from %s", sock.Remote()) - t.Lock() - delete(t.links, sock.Remote()) - t.Unlock() - }() - - // listen for inbound messages + // listen for inbound messages. + // only save the link once connected. + // we do this inside liste t.listen(link) }) @@ -473,6 +514,7 @@ func (t *tun) connect() error { log.Debugf("Tunnel failed to establish node link to %s: %v", node, err) continue } + // save the link t.links[node] = link } diff --git a/tunnel/listener.go b/tunnel/listener.go index 3002e7b6..b953601b 100644 --- a/tunnel/listener.go +++ b/tunnel/listener.go @@ -41,6 +41,8 @@ func (t *tunListener) process() { id: m.id, // the session id session: m.session, + // is loopback conn + loopback: m.loopback, // close chan closed: make(chan bool), // recv called by the acceptor diff --git a/tunnel/socket.go b/tunnel/socket.go index 2590a48e..921d4cf0 100644 --- a/tunnel/socket.go +++ b/tunnel/socket.go @@ -25,8 +25,10 @@ type socket struct { recv chan *message // wait until we have a connection wait chan bool - // outbound marks the socket as outbound + // outbound marks the socket as outbound dialled connection outbound bool + // lookback marks the socket as a loopback on the inbound + loopback bool } // message is sent over the send channel @@ -37,6 +39,8 @@ type message struct { session string // outbound marks the message as outbound outbound bool + // loopback marks the message intended for loopback + loopback bool // transport data data *transport.Message } @@ -80,6 +84,7 @@ func (s *socket) Send(m *transport.Message) error { id: s.id, session: s.session, outbound: s.outbound, + loopback: s.loopback, data: data, } log.Debugf("Appending %+v to send backlog", msg) From 8606f1e143ba6ccc3b126b1bd29fe4914f2658ee Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Thu, 29 Aug 2019 11:45:47 +0100 Subject: [PATCH 24/93] Set the route.Metric before updating routing table --- network/default.go | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/network/default.go b/network/default.go index 3f917787..ca8328b3 100644 --- a/network/default.go +++ b/network/default.go @@ -376,7 +376,43 @@ func (n *network) acceptCtrlConn(l tunnel.Listener, recv chan *transport.Message } } -// process processes network advertisements +// setRouteMetric calculates metric of the route and updates it in place +// - Local route metric is 1 +// - Routes with ID of adjacent neighbour are 10 +// - Routes of neighbours of the advertiser are 100 +// - Routes beyond your neighbourhood are 1000 +func (n *network) setRouteMetric(route *router.Route) { + // we are the origin of the route + if route.Router == n.options.Id { + route.Metric = 1 + return + } + + n.RLock() + // check if the route origin is our neighbour + if _, ok := n.neighbours[route.Router]; ok { + route.Metric = 10 + n.RUnlock() + return + } + + // check if the route origin is the neighbour of our neighbour + for _, node := range n.neighbours { + for id, _ := range node.neighbours { + if route.Router == id { + route.Metric = 100 + n.RUnlock() + return + } + } + } + n.RUnlock() + + // the origin of the route is beyond our neighbourhood + route.Metric = 1000 +} + +// processCtrlChan processes messages received on ControlChannel func (n *network) processCtrlChan(l tunnel.Listener) { // receive control message queue recv := make(chan *transport.Message, 128) @@ -406,6 +442,13 @@ func (n *network) processCtrlChan(l tunnel.Listener) { Link: event.Route.Link, Metric: int(event.Route.Metric), } + // set the route metric + n.setRouteMetric(&route) + // throw away metric bigger than 1000 + if route.Metric > 1000 { + continue + } + // create router event e := &router.Event{ Type: router.EventType(event.Type), Timestamp: time.Unix(0, pbRtrAdvert.Timestamp), From 00ab58f61b8eac3dc990c5ba39216fe72d4ddeb4 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Thu, 29 Aug 2019 12:42:27 +0100 Subject: [PATCH 25/93] Fix loopback cruft --- tunnel/default.go | 40 +++++++++++++++++++++++++--------------- tunnel/link.go | 13 +++++++++++++ tunnel/listener.go | 2 ++ tunnel/socket.go | 10 ++++++++++ 4 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 tunnel/link.go diff --git a/tunnel/default.go b/tunnel/default.go index bd6af7dd..f03016ed 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -53,6 +53,8 @@ type link struct { // which we define for ourselves id string // whether its a loopback connection + // this flag is used by the transport listener + // which accepts inbound quic connections loopback bool // whether its actually connected // dialled side sets it to connected @@ -183,7 +185,7 @@ func (t *tun) process() { } // set message head - newMsg.Header["Micro-Tunnel"] = "message" + newMsg.Header["Micro-Tunnel"] = msg.typ // set the tunnel id on the outgoing message newMsg.Header["Micro-Tunnel-Id"] = msg.id @@ -196,9 +198,11 @@ func (t *tun) process() { // send the message via the interface t.Lock() + if len(t.links) == 0 { log.Debugf("No links to send to") } + for node, link := range t.links { // if the link is not connected skip it if !link.connected { @@ -206,6 +210,13 @@ func (t *tun) process() { continue } + // if we're picking the link check the id + // this is where we explicitly set the link + // in a message received via the listen method + if len(msg.link) > 0 && link.id != msg.link { + continue + } + // if the link was a loopback accepted connection // and the message is being sent outbound via // a dialled connection don't use this link @@ -219,6 +230,7 @@ func (t *tun) process() { continue } + // send the message via the current link log.Debugf("Sending %+v to %s", newMsg, node) if err := link.Send(newMsg); err != nil { log.Debugf("Tunnel error sending %+v to %s: %v", newMsg, node, err) @@ -226,6 +238,7 @@ func (t *tun) process() { continue } } + t.Unlock() case <-t.closed: return @@ -283,10 +296,11 @@ func (t *tun) listen(link *link) { log.Debugf("Tunnel link %s closing connection", link.Remote()) // TODO: handle the close message // maybe report io.EOF or kill the link - continue + return case "keepalive": log.Debugf("Tunnel link %s received keepalive", link.Remote()) t.Lock() + // save the keepalive link.lastKeepAlive = time.Now() t.Unlock() continue @@ -300,6 +314,7 @@ func (t *tun) listen(link *link) { // if its not connected throw away the link if !link.connected { + log.Debugf("Tunnel link %s not connected", link.id) return } @@ -388,6 +403,7 @@ func (t *tun) listen(link *link) { id: id, session: session, data: tmsg, + link: link.id, loopback: loopback, } @@ -449,13 +465,10 @@ func (t *tun) setupLink(node string) (*link, error) { } // create a new link - link := &link{ - Socket: c, - id: uuid.New().String(), - // we made the outbound connection - // and sent the connect message - connected: true, - } + link := newLink(c) + link.connected = true + // we made the outbound connection + // and sent the connect message // process incoming messages go t.listen(link) @@ -482,10 +495,7 @@ func (t *tun) connect() error { log.Debugf("Tunnel accepted connection from %s", sock.Remote()) // create a new link - link := &link{ - Socket: sock, - id: uuid.New().String(), - } + link := newLink(sock) // listen for inbound messages. // only save the link once connected. @@ -493,8 +503,8 @@ func (t *tun) connect() error { t.listen(link) }) - t.Lock() - defer t.Unlock() + t.RLock() + defer t.RUnlock() // still connected but the tunnel died if err != nil && t.connected { diff --git a/tunnel/link.go b/tunnel/link.go new file mode 100644 index 00000000..6b8f30aa --- /dev/null +++ b/tunnel/link.go @@ -0,0 +1,13 @@ +package tunnel + +import ( + "github.com/google/uuid" + "github.com/micro/go-micro/transport" +) + +func newLink(s transport.Socket) *link { + return &link{ + Socket: s, + id: uuid.New().String(), + } +} diff --git a/tunnel/listener.go b/tunnel/listener.go index b953601b..42aadfd1 100644 --- a/tunnel/listener.go +++ b/tunnel/listener.go @@ -43,6 +43,8 @@ func (t *tunListener) process() { session: m.session, // is loopback conn loopback: m.loopback, + // the link the message was received on + link: m.link, // close chan closed: make(chan bool), // recv called by the acceptor diff --git a/tunnel/socket.go b/tunnel/socket.go index 921d4cf0..789d9555 100644 --- a/tunnel/socket.go +++ b/tunnel/socket.go @@ -29,10 +29,14 @@ type socket struct { outbound bool // lookback marks the socket as a loopback on the inbound loopback bool + // the link on which this message was received + link string } // message is sent over the send channel type message struct { + // type of message + typ string // tunnel id id string // the session id @@ -41,6 +45,8 @@ type message struct { outbound bool // loopback marks the message intended for loopback loopback bool + // the link to send the message on + link string // transport data data *transport.Message } @@ -81,11 +87,15 @@ func (s *socket) Send(m *transport.Message) error { // append to backlog msg := &message{ + typ: "message", id: s.id, session: s.session, outbound: s.outbound, loopback: s.loopback, data: data, + // specify the link on which to send this + // it will be blank for dialled sockets + link: s.link, } log.Debugf("Appending %+v to send backlog", msg) s.send <- msg From 7d033818cf9252f6528fb02590dc00decc517fb5 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Thu, 29 Aug 2019 13:10:06 +0100 Subject: [PATCH 26/93] if the service name is blank, barf --- proxy/mucp/mucp.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/proxy/mucp/mucp.go b/proxy/mucp/mucp.go index ec95f407..8b25d22d 100644 --- a/proxy/mucp/mucp.go +++ b/proxy/mucp/mucp.go @@ -161,9 +161,6 @@ func (p *Proxy) manageRouteCache(route router.Route, action string) error { } p.Routes[route.Service][route.Hash()] = route case "delete": - if _, ok := p.Routes[route.Service]; !ok { - return fmt.Errorf("route not found") - } delete(p.Routes[route.Service], route.Hash()) default: return fmt.Errorf("unknown action: %s", action) @@ -219,6 +216,11 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server // endpoint to call endpoint := req.Endpoint() + if len(service) == 0 { + b, _ := req.Read() + return errors.BadRequest("go.micro.proxy", "service name is blank") + } + // are we network routing or local routing if len(p.Links) == 0 { local = true From 721c5e6857bde8b8f86cb691c71b6d34481fef26 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Thu, 29 Aug 2019 13:11:20 +0100 Subject: [PATCH 27/93] fix broken build --- proxy/mucp/mucp.go | 1 - 1 file changed, 1 deletion(-) diff --git a/proxy/mucp/mucp.go b/proxy/mucp/mucp.go index 8b25d22d..9fccc6cc 100644 --- a/proxy/mucp/mucp.go +++ b/proxy/mucp/mucp.go @@ -217,7 +217,6 @@ func (p *Proxy) ServeRequest(ctx context.Context, req server.Request, rsp server endpoint := req.Endpoint() if len(service) == 0 { - b, _ := req.Read() return errors.BadRequest("go.micro.proxy", "service name is blank") } From 74795150993538d263c46ec429d13e3c8a34eeda Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Thu, 29 Aug 2019 14:53:30 +0100 Subject: [PATCH 28/93] add the ability to provide seed nodes to the network --- network/default.go | 13 ++++++++++++- network/options.go | 10 ++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/network/default.go b/network/default.go index ca8328b3..204bcd81 100644 --- a/network/default.go +++ b/network/default.go @@ -75,6 +75,7 @@ func newNetwork(opts ...Option) Network { // init tunnel address to the network bind address options.Tunnel.Init( tunnel.Address(options.Address), + tunnel.Nodes(options.Nodes...), ) // init router Id to the network id @@ -135,10 +136,20 @@ func (n *network) resolveNodes() ([]string, error) { return nil, err } + nodeMap := make(map[string]bool) + // collect network node addresses - nodes := make([]string, len(records)) + var nodes []string for i, record := range records { nodes[i] = record.Address + nodeMap[record.Address] = true + } + + // append seed nodes if we have them + for _, node := range n.options.Nodes { + if _, ok := nodeMap[node]; !ok { + nodes = append(nodes, node) + } } return nodes, nil diff --git a/network/options.go b/network/options.go index 5894b857..e670cf4c 100644 --- a/network/options.go +++ b/network/options.go @@ -20,6 +20,8 @@ type Options struct { Name string // Address to bind to Address string + // Nodes is a list of seed nodes + Nodes []string // Tunnel is network tunnel Tunnel tunnel.Tunnel // Router is network router @@ -51,6 +53,14 @@ func Address(a string) Option { } } +// Nodes is a list of seed nodes used along +// with resolved node +func Nodes(n ...string) Option { + return func(o *Options) { + o.Nodes = n + } +} + // Tunnel sets the network tunnel func Tunnel(t tunnel.Tunnel) Option { return func(o *Options) { From 3d03fe40768db68f03faa3f8ac1d57fee22b3c64 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Thu, 29 Aug 2019 15:09:01 +0100 Subject: [PATCH 29/93] Fix panic for nil slice --- network/default.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/default.go b/network/default.go index 204bcd81..2e03bf9a 100644 --- a/network/default.go +++ b/network/default.go @@ -140,8 +140,8 @@ func (n *network) resolveNodes() ([]string, error) { // collect network node addresses var nodes []string - for i, record := range records { - nodes[i] = record.Address + for _, record := range records { + nodes = append(nodes, record.Address) nodeMap[record.Address] = true } From ffa6b551f4bbdcfbff0beb7a3419b61ea8dde932 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Thu, 29 Aug 2019 15:42:07 +0100 Subject: [PATCH 30/93] Don't override the neighbours. --- network/default.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/network/default.go b/network/default.go index 2e03bf9a..e5830e21 100644 --- a/network/default.go +++ b/network/default.go @@ -309,11 +309,11 @@ func (n *network) announce(client transport.Client) { nodes := make([]*pbNet.Node, len(n.neighbours)) i := 0 for id, _ := range n.neighbours { - pbNode := &pbNet.Node{ + nodes[i] = &pbNet.Node{ Id: id, Address: n.neighbours[id].address, } - nodes[i] = pbNode + i++ } n.RUnlock() From f50bd400f89f7c62bfe82028a2c2ec4dcbc13598 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Thu, 29 Aug 2019 16:21:30 +0100 Subject: [PATCH 31/93] Only emit event if Update actually happens --- router/table.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/router/table.go b/router/table.go index 42a1be47..c35f9a89 100644 --- a/router/table.go +++ b/router/table.go @@ -105,8 +105,10 @@ func (t *table) Update(r Route) error { return nil } - t.routes[service][sum] = r - go t.sendEvent(&Event{Type: Update, Timestamp: time.Now(), Route: r}) + if _, ok := t.routes[service][sum]; !ok { + t.routes[service][sum] = r + go t.sendEvent(&Event{Type: Update, Timestamp: time.Now(), Route: r}) + } return nil } From 4f788c6fc7ab036de4c293c691b68a46cd4e88d8 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Thu, 29 Aug 2019 16:25:21 +0100 Subject: [PATCH 32/93] Only emit the events when actually deleting the route --- router/table.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/router/table.go b/router/table.go index c35f9a89..c61b560c 100644 --- a/router/table.go +++ b/router/table.go @@ -83,8 +83,10 @@ func (t *table) Delete(r Route) error { return ErrRouteNotFound } - delete(t.routes[service], sum) - go t.sendEvent(&Event{Type: Delete, Timestamp: time.Now(), Route: r}) + if _, ok := t.routes[service][sum]; ok { + delete(t.routes[service], sum) + go t.sendEvent(&Event{Type: Delete, Timestamp: time.Now(), Route: r}) + } return nil } From e7d8cdda449c9c69f339a64b79904aba76c83b92 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Thu, 29 Aug 2019 16:58:07 +0100 Subject: [PATCH 33/93] Avoid duplicate debug logs. --- tunnel/default.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tunnel/default.go b/tunnel/default.go index f03016ed..fc94c5a5 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -447,7 +447,7 @@ func (t *tun) keepalive(link *link) { // setupLink connects to node and returns link if successful // It returns error if the link failed to be established func (t *tun) setupLink(node string) (*link, error) { - log.Debugf("Tunnel dialing %s", node) + log.Debugf("Tunnel setting up link: %s", node) c, err := t.options.Transport.Dial(node) if err != nil { log.Debugf("Tunnel failed to connect to %s: %v", node, err) From e1d56fbf58f214f962a896fdbcc5cc60c87d9102 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Thu, 29 Aug 2019 17:21:43 +0100 Subject: [PATCH 34/93] switch warn to error logging --- util/log/log.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/util/log/log.go b/util/log/log.go index 0b911aae..c8ae4acc 100644 --- a/util/log/log.go +++ b/util/log/log.go @@ -14,7 +14,7 @@ type Level int const ( LevelFatal Level = iota LevelInfo - LevelWarn + LevelError LevelDebug LevelTrace ) @@ -29,16 +29,16 @@ var ( func init() { switch os.Getenv("MICRO_LOG_LEVEL") { + case "trace": + level = LevelTrace case "debug": level = LevelDebug case "info": level = LevelInfo - case "trace": - level = LevelTrace + case "error": + level = LevelError case "fatal": level = LevelFatal - case "warn": - level = LevelWarn } } @@ -98,14 +98,14 @@ func Infof(format string, v ...interface{}) { WithLevelf(LevelInfo, format, v...) } -// Warn provides warn level logging -func Warn(v ...interface{}) { - WithLevel(LevelWarn, v...) +// Error provides warn level logging +func Error(v ...interface{}) { + WithLevel(LevelError, v...) } -// Warnf provides warn level logging -func Warnf(format string, v ...interface{}) { - WithLevelf(LevelWarn, format, v...) +// Errorf provides warn level logging +func Errorf(format string, v ...interface{}) { + WithLevelf(LevelError, format, v...) } // Fatal logs with Log and then exits with os.Exit(1) From e955e3f798c4447f3a7b1378104ce70024e53c81 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Fri, 30 Aug 2019 00:04:46 +0100 Subject: [PATCH 35/93] Avoid routes that route back to node without its being direct GW to dest --- network/default.go | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/network/default.go b/network/default.go index e5830e21..186513db 100644 --- a/network/default.go +++ b/network/default.go @@ -14,7 +14,7 @@ import ( "github.com/micro/go-micro/server" "github.com/micro/go-micro/transport" "github.com/micro/go-micro/tunnel" - trn "github.com/micro/go-micro/tunnel/transport" + tun "github.com/micro/go-micro/tunnel/transport" "github.com/micro/go-micro/util/log" ) @@ -84,8 +84,8 @@ func newNetwork(opts ...Option) Network { ) // create tunnel client with tunnel transport - tunTransport := trn.NewTransport( - trn.WithTunnel(options.Tunnel), + tunTransport := tun.NewTransport( + tun.WithTunnel(options.Tunnel), ) // server is network server @@ -439,11 +439,42 @@ func (n *network) processCtrlChan(l tunnel.Listener) { case "advert": pbRtrAdvert := &pbRtr.Advert{} if err := proto.Unmarshal(m.Body, pbRtrAdvert); err != nil { + log.Debugf("Network fail to unmarshal advert message: %v", err) continue } + // loookup advertising node in our neighbourhood + n.RLock() + advertNode, ok := n.neighbours[pbRtrAdvert.Id] + if !ok { + // advertising node has not been registered as our neighbour, yet + // let's add it to the map of our neighbours + advertNode = &node{ + id: pbRtrAdvert.Id, + neighbours: make(map[string]*node), + } + n.neighbours[pbRtrAdvert.Id] = advertNode + } + n.RUnlock() + var events []*router.Event for _, event := range pbRtrAdvert.Events { + // set the address of the advertising node + // we know Route.Gateway is the address of advertNode + // NOTE: this is true only when advertNode had not been registered + // as our neighbour when we received the advert from it + if advertNode.address == "" { + advertNode.address = event.Route.Gateway + } + // if advertising node id is not the same as Route.Router + // we know the advertising node is not the origin of the route + if advertNode.id != event.Route.Router { + // if the origin router is not in the advertising node neighbourhood + // we can't rule out potential routing loops so we bail here + if _, ok := advertNode.neighbours[event.Route.Router]; !ok { + continue + } + } route := router.Route{ Service: event.Route.Service, Address: event.Route.Address, From ff81e4b2468a4c2d6fed53a7e2671a3647dee179 Mon Sep 17 00:00:00 2001 From: Yumin Wu Date: Fri, 30 Aug 2019 16:20:58 +0800 Subject: [PATCH 36/93] Load consul source --- config/source/consul/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/source/consul/README.md b/config/source/consul/README.md index 2ba45e5b..28006fc4 100644 --- a/config/source/consul/README.md +++ b/config/source/consul/README.md @@ -44,6 +44,6 @@ Load the source into config // Create new config conf := config.NewConfig() -// Load file source +// Load consul source conf.Load(consulSource) ``` From b37837ad92826824fbd9d4e61e3e2d14d3da84fe Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Fri, 30 Aug 2019 12:29:26 +0100 Subject: [PATCH 37/93] Add proto definitions for network introspection. --- network/proto/network.micro.go | 105 ++++++++++++++++++++++++++++++ network/proto/network.pb.go | 115 ++++++++++++++++++++++++++++----- network/proto/network.proto | 17 +++++ 3 files changed, 221 insertions(+), 16 deletions(-) diff --git a/network/proto/network.micro.go b/network/proto/network.micro.go index 814e6dba..fa0eefaf 100644 --- a/network/proto/network.micro.go +++ b/network/proto/network.micro.go @@ -6,9 +6,16 @@ package go_micro_network import ( fmt "fmt" proto "github.com/golang/protobuf/proto" + proto1 "github.com/micro/go-micro/router/proto" math "math" ) +import ( + context "context" + client "github.com/micro/go-micro/client" + server "github.com/micro/go-micro/server" +) + // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf @@ -19,3 +26,101 @@ var _ = math.Inf // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ client.Option +var _ server.Option + +// Client API for Network service + +type NetworkService interface { + ListRoutes(ctx context.Context, in *proto1.Request, opts ...client.CallOption) (*proto1.ListResponse, error) + ListNodes(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) + ListNeighbours(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) +} + +type networkService struct { + c client.Client + name string +} + +func NewNetworkService(name string, c client.Client) NetworkService { + if c == nil { + c = client.NewClient() + } + if len(name) == 0 { + name = "go.micro.network" + } + return &networkService{ + c: c, + name: name, + } +} + +func (c *networkService) ListRoutes(ctx context.Context, in *proto1.Request, opts ...client.CallOption) (*proto1.ListResponse, error) { + req := c.c.NewRequest(c.name, "Network.ListRoutes", in) + out := new(proto1.ListResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *networkService) ListNodes(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) { + req := c.c.NewRequest(c.name, "Network.ListNodes", in) + out := new(ListResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *networkService) ListNeighbours(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) { + req := c.c.NewRequest(c.name, "Network.ListNeighbours", in) + out := new(ListResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Network service + +type NetworkHandler interface { + ListRoutes(context.Context, *proto1.Request, *proto1.ListResponse) error + ListNodes(context.Context, *ListRequest, *ListResponse) error + ListNeighbours(context.Context, *ListRequest, *ListResponse) error +} + +func RegisterNetworkHandler(s server.Server, hdlr NetworkHandler, opts ...server.HandlerOption) error { + type network interface { + ListRoutes(ctx context.Context, in *proto1.Request, out *proto1.ListResponse) error + ListNodes(ctx context.Context, in *ListRequest, out *ListResponse) error + ListNeighbours(ctx context.Context, in *ListRequest, out *ListResponse) error + } + type Network struct { + network + } + h := &networkHandler{hdlr} + return s.Handle(s.NewHandler(&Network{h}, opts...)) +} + +type networkHandler struct { + NetworkHandler +} + +func (h *networkHandler) ListRoutes(ctx context.Context, in *proto1.Request, out *proto1.ListResponse) error { + return h.NetworkHandler.ListRoutes(ctx, in, out) +} + +func (h *networkHandler) ListNodes(ctx context.Context, in *ListRequest, out *ListResponse) error { + return h.NetworkHandler.ListNodes(ctx, in, out) +} + +func (h *networkHandler) ListNeighbours(ctx context.Context, in *ListRequest, out *ListResponse) error { + return h.NetworkHandler.ListNeighbours(ctx, in, out) +} diff --git a/network/proto/network.pb.go b/network/proto/network.pb.go index 1a1d5948..9b5ccbaf 100644 --- a/network/proto/network.pb.go +++ b/network/proto/network.pb.go @@ -6,6 +6,7 @@ package go_micro_network import ( fmt "fmt" proto "github.com/golang/protobuf/proto" + _ "github.com/micro/go-micro/router/proto" math "math" ) @@ -20,6 +21,78 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package +// Empty request +type ListRequest struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListRequest) Reset() { *m = ListRequest{} } +func (m *ListRequest) String() string { return proto.CompactTextString(m) } +func (*ListRequest) ProtoMessage() {} +func (*ListRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_8571034d60397816, []int{0} +} + +func (m *ListRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListRequest.Unmarshal(m, b) +} +func (m *ListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListRequest.Marshal(b, m, deterministic) +} +func (m *ListRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListRequest.Merge(m, src) +} +func (m *ListRequest) XXX_Size() int { + return xxx_messageInfo_ListRequest.Size(m) +} +func (m *ListRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListRequest proto.InternalMessageInfo + +// ListResponse is returned by ListNodes and ListNeighbours +type ListResponse struct { + Nodes []*Node `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListResponse) Reset() { *m = ListResponse{} } +func (m *ListResponse) String() string { return proto.CompactTextString(m) } +func (*ListResponse) ProtoMessage() {} +func (*ListResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_8571034d60397816, []int{1} +} + +func (m *ListResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListResponse.Unmarshal(m, b) +} +func (m *ListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListResponse.Marshal(b, m, deterministic) +} +func (m *ListResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListResponse.Merge(m, src) +} +func (m *ListResponse) XXX_Size() int { + return xxx_messageInfo_ListResponse.Size(m) +} +func (m *ListResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListResponse proto.InternalMessageInfo + +func (m *ListResponse) GetNodes() []*Node { + if m != nil { + return m.Nodes + } + return nil +} + // Node is network node type Node struct { // node ide @@ -35,7 +108,7 @@ func (m *Node) Reset() { *m = Node{} } func (m *Node) String() string { return proto.CompactTextString(m) } func (*Node) ProtoMessage() {} func (*Node) Descriptor() ([]byte, []int) { - return fileDescriptor_8571034d60397816, []int{0} + return fileDescriptor_8571034d60397816, []int{2} } func (m *Node) XXX_Unmarshal(b []byte) error { @@ -83,7 +156,7 @@ func (m *Connect) Reset() { *m = Connect{} } func (m *Connect) String() string { return proto.CompactTextString(m) } func (*Connect) ProtoMessage() {} func (*Connect) Descriptor() ([]byte, []int) { - return fileDescriptor_8571034d60397816, []int{1} + return fileDescriptor_8571034d60397816, []int{3} } func (m *Connect) XXX_Unmarshal(b []byte) error { @@ -124,7 +197,7 @@ func (m *Close) Reset() { *m = Close{} } func (m *Close) String() string { return proto.CompactTextString(m) } func (*Close) ProtoMessage() {} func (*Close) Descriptor() ([]byte, []int) { - return fileDescriptor_8571034d60397816, []int{2} + return fileDescriptor_8571034d60397816, []int{4} } func (m *Close) XXX_Unmarshal(b []byte) error { @@ -167,7 +240,7 @@ func (m *Neighbour) Reset() { *m = Neighbour{} } func (m *Neighbour) String() string { return proto.CompactTextString(m) } func (*Neighbour) ProtoMessage() {} func (*Neighbour) Descriptor() ([]byte, []int) { - return fileDescriptor_8571034d60397816, []int{3} + return fileDescriptor_8571034d60397816, []int{5} } func (m *Neighbour) XXX_Unmarshal(b []byte) error { @@ -203,6 +276,8 @@ func (m *Neighbour) GetNeighbours() []*Node { } func init() { + proto.RegisterType((*ListRequest)(nil), "go.micro.network.ListRequest") + proto.RegisterType((*ListResponse)(nil), "go.micro.network.ListResponse") proto.RegisterType((*Node)(nil), "go.micro.network.Node") proto.RegisterType((*Connect)(nil), "go.micro.network.Connect") proto.RegisterType((*Close)(nil), "go.micro.network.Close") @@ -212,16 +287,24 @@ func init() { func init() { proto.RegisterFile("network.proto", fileDescriptor_8571034d60397816) } var fileDescriptor_8571034d60397816 = []byte{ - // 173 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xcd, 0x4b, 0x2d, 0x29, - 0xcf, 0x2f, 0xca, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0x48, 0xcf, 0xd7, 0xcb, 0xcd, - 0x4c, 0x2e, 0xca, 0xd7, 0x83, 0x8a, 0x2b, 0x19, 0x70, 0xb1, 0xf8, 0xe5, 0xa7, 0xa4, 0x0a, 0xf1, - 0x71, 0x31, 0x65, 0xa6, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0x31, 0x65, 0xa6, 0x08, 0x49, - 0x70, 0xb1, 0x27, 0xa6, 0xa4, 0x14, 0xa5, 0x16, 0x17, 0x4b, 0x30, 0x81, 0x05, 0x61, 0x5c, 0x25, - 0x53, 0x2e, 0x76, 0xe7, 0xfc, 0xbc, 0xbc, 0xd4, 0xe4, 0x12, 0x21, 0x2d, 0x2e, 0x96, 0xbc, 0xfc, - 0x94, 0x54, 0xb0, 0x36, 0x6e, 0x23, 0x31, 0x3d, 0x74, 0xd3, 0xf5, 0x40, 0x46, 0x07, 0x81, 0xd5, - 0x28, 0x19, 0x73, 0xb1, 0x3a, 0xe7, 0xe4, 0x17, 0xa7, 0x92, 0xa4, 0x29, 0x9f, 0x8b, 0xd3, 0x2f, - 0x35, 0x33, 0x3d, 0x23, 0x29, 0xbf, 0xb4, 0x88, 0x14, 0x8d, 0x42, 0x66, 0x5c, 0x5c, 0x79, 0x30, - 0x8d, 0xc5, 0x12, 0xcc, 0x0a, 0xcc, 0x78, 0x74, 0x20, 0xa9, 0x4c, 0x62, 0x03, 0x87, 0x93, 0x31, - 0x20, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x74, 0x00, 0x71, 0x38, 0x01, 0x00, 0x00, + // 302 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x52, 0x5d, 0x4b, 0xc3, 0x30, + 0x14, 0xb5, 0xfb, 0x70, 0xec, 0xce, 0x0d, 0xc9, 0x83, 0x84, 0xc2, 0x64, 0xe4, 0x69, 0x88, 0xa6, + 0xb2, 0xa2, 0x4f, 0xbe, 0xed, 0xc1, 0x97, 0x51, 0xb0, 0xff, 0xc0, 0x36, 0xa1, 0x0b, 0xba, 0xdc, + 0x99, 0xa4, 0xf8, 0xc3, 0xfd, 0x03, 0xd2, 0xb4, 0x9b, 0xc5, 0x31, 0x61, 0xf8, 0x96, 0x73, 0xcf, + 0x3d, 0xe7, 0x5e, 0xee, 0x09, 0x8c, 0xb5, 0x74, 0x9f, 0x68, 0xde, 0xf8, 0xd6, 0xa0, 0x43, 0x72, + 0x59, 0x20, 0xdf, 0xa8, 0xdc, 0x20, 0x6f, 0xea, 0x61, 0x5c, 0x28, 0xb7, 0x2e, 0x33, 0x9e, 0xe3, + 0x26, 0xf2, 0x4c, 0x54, 0xe0, 0x5d, 0xfd, 0x30, 0x58, 0x3a, 0x69, 0x22, 0xaf, 0x6c, 0x40, 0x6d, + 0xc3, 0xc6, 0x30, 0x5a, 0x29, 0xeb, 0x52, 0xf9, 0x51, 0x4a, 0xeb, 0xd8, 0x13, 0x5c, 0xd4, 0xd0, + 0x6e, 0x51, 0x5b, 0x49, 0x6e, 0xa1, 0xaf, 0x51, 0x48, 0x4b, 0x83, 0x59, 0x77, 0x3e, 0x5a, 0x5c, + 0xf1, 0xdf, 0x53, 0x79, 0x82, 0x42, 0xa6, 0x75, 0x13, 0xbb, 0x87, 0x5e, 0x05, 0xc9, 0x04, 0x3a, + 0x4a, 0xd0, 0x60, 0x16, 0xcc, 0x87, 0x69, 0x47, 0x09, 0x42, 0x61, 0xf0, 0x2a, 0x84, 0x91, 0xd6, + 0xd2, 0x8e, 0x2f, 0xee, 0x20, 0x7b, 0x80, 0xc1, 0x12, 0xb5, 0x96, 0xb9, 0x23, 0x37, 0xd0, 0xab, + 0x5c, 0xbc, 0xec, 0xf8, 0x24, 0xdf, 0xc3, 0x62, 0xe8, 0x2f, 0xdf, 0xd1, 0xca, 0x93, 0x44, 0x08, + 0xc3, 0x44, 0xaa, 0x62, 0x9d, 0x61, 0x69, 0x4e, 0x11, 0x92, 0x47, 0x00, 0xbd, 0x13, 0x5a, 0xda, + 0xfd, 0xf3, 0x12, 0xad, 0xce, 0xc5, 0x57, 0x00, 0x83, 0xa4, 0x26, 0xc9, 0x33, 0x80, 0x3f, 0x6c, + 0x75, 0x7b, 0x4b, 0xe8, 0x8f, 0xba, 0x49, 0xa3, 0x09, 0x20, 0x9c, 0x1e, 0x30, 0xed, 0x3c, 0xd8, + 0x19, 0x59, 0xc1, 0xb0, 0xaa, 0x54, 0xc3, 0x2c, 0x99, 0x1e, 0x6e, 0xd1, 0x4a, 0x33, 0xbc, 0x3e, + 0x46, 0xef, 0xdd, 0x5e, 0x60, 0xe2, 0xdd, 0xf6, 0x4b, 0xff, 0xdb, 0x32, 0x3b, 0xf7, 0x1f, 0x2b, + 0xfe, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x19, 0x6e, 0xfe, 0x91, 0xb0, 0x02, 0x00, 0x00, } diff --git a/network/proto/network.proto b/network/proto/network.proto index 10483116..2b3e3a2c 100644 --- a/network/proto/network.proto +++ b/network/proto/network.proto @@ -2,6 +2,23 @@ syntax = "proto3"; package go.micro.network; +import "github.com/micro/go-micro/router/proto/router.proto"; + +// Network service is usesd to gain visibility into networks +service Network { + rpc ListRoutes(go.micro.router.Request) returns (go.micro.router.ListResponse) {}; + rpc ListNodes(ListRequest) returns (ListResponse) {}; + rpc ListNeighbours(ListRequest) returns (ListResponse) {}; +} + +// Empty request +message ListRequest {} + +// ListResponse is returned by ListNodes and ListNeighbours +message ListResponse { + repeated Node nodes = 1; +} + // Node is network node message Node { // node ide From 6fa9d7270f779181e989b33f1212d4b938a27ebf Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Fri, 30 Aug 2019 20:05:00 +0100 Subject: [PATCH 38/93] Rename Tunnel ID to Channel --- network/default.go | 8 +- tunnel/default.go | 231 ++++++++++++++++--------------- tunnel/link.go | 24 ++++ tunnel/listener.go | 48 ++++--- tunnel/options.go | 12 ++ tunnel/{socket.go => session.go} | 67 ++++++--- tunnel/transport/listener.go | 2 +- tunnel/tunnel.go | 18 +-- 8 files changed, 246 insertions(+), 164 deletions(-) rename tunnel/{socket.go => session.go} (60%) diff --git a/network/default.go b/network/default.go index 186513db..1b5669c6 100644 --- a/network/default.go +++ b/network/default.go @@ -179,10 +179,10 @@ func (n *network) resolve() { } // handleNetConn handles network announcement messages -func (n *network) handleNetConn(conn tunnel.Conn, msg chan *transport.Message) { +func (n *network) handleNetConn(sess tunnel.Session, msg chan *transport.Message) { for { m := new(transport.Message) - if err := conn.Recv(m); err != nil { + if err := sess.Recv(m); err != nil { // TODO: should we bail here? log.Debugf("Network tunnel [%s] receive error: %v", NetworkChannel, err) return @@ -349,10 +349,10 @@ func (n *network) announce(client transport.Client) { } // handleCtrlConn handles ControlChannel connections -func (n *network) handleCtrlConn(conn tunnel.Conn, msg chan *transport.Message) { +func (n *network) handleCtrlConn(sess tunnel.Session, msg chan *transport.Message) { for { m := new(transport.Message) - if err := conn.Recv(m); err != nil { + if err := sess.Recv(m); err != nil { // TODO: should we bail here? log.Debugf("Network tunnel advert receive error: %v", err) return diff --git a/tunnel/default.go b/tunnel/default.go index fc94c5a5..51468164 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -1,9 +1,8 @@ package tunnel import ( - "crypto/sha256" "errors" - "fmt" + "strings" "sync" "time" @@ -25,7 +24,10 @@ type tun struct { sync.RWMutex - // tunnel token + // the unique id for this tunnel + id string + + // tunnel token for authentication token string // to indicate if we're connected or not @@ -37,8 +39,8 @@ type tun struct { // close channel closed chan bool - // a map of sockets based on Micro-Tunnel-Id - sockets map[string]*socket + // a map of sessions based on Micro-Tunnel-Channel + sessions map[string]*session // outbound links links map[string]*link @@ -47,25 +49,6 @@ type tun struct { listener transport.Listener } -type link struct { - transport.Socket - // unique id of this link e.g uuid - // which we define for ourselves - id string - // whether its a loopback connection - // this flag is used by the transport listener - // which accepts inbound quic connections - loopback bool - // whether its actually connected - // dialled side sets it to connected - // after sending the message. the - // listener waits for the connect - connected bool - // the last time we received a keepalive - // on this link from the remote side - lastKeepAlive time.Time -} - // create new tunnel on top of a link func newTunnel(opts ...Option) *tun { options := DefaultOptions() @@ -74,12 +57,13 @@ func newTunnel(opts ...Option) *tun { } return &tun{ - options: options, - token: uuid.New().String(), - send: make(chan *message, 128), - closed: make(chan bool), - sockets: make(map[string]*socket), - links: make(map[string]*link), + options: options, + id: options.Id, + token: options.Token, + send: make(chan *message, 128), + closed: make(chan bool), + sessions: make(map[string]*session), + links: make(map[string]*link), } } @@ -93,51 +77,48 @@ func (t *tun) Init(opts ...Option) error { return nil } -// getSocket returns a socket from the internal socket map. -// It does this based on the Micro-Tunnel-Id and Micro-Tunnel-Session -func (t *tun) getSocket(id, session string) (*socket, bool) { - // get the socket +// getSession returns a session from the internal session map. +// It does this based on the Micro-Tunnel-Channel and Micro-Tunnel-Session +func (t *tun) getSession(channel, session string) (*session, bool) { + // get the session t.RLock() - s, ok := t.sockets[id+session] + s, ok := t.sessions[channel+session] t.RUnlock() return s, ok } -// newSocket creates a new socket and saves it -func (t *tun) newSocket(id, session string) (*socket, bool) { - // hash the id - h := sha256.New() - h.Write([]byte(id)) - id = fmt.Sprintf("%x", h.Sum(nil)) - - // new socket - s := &socket{ - id: id, - session: session, +// newSession creates a new session and saves it +func (t *tun) newSession(channel, sessionId string) (*session, bool) { + // new session + s := &session{ + id: t.id, + channel: channel, + session: sessionId, closed: make(chan bool), recv: make(chan *message, 128), send: t.send, wait: make(chan bool), + errChan: make(chan error, 1), } - // save socket + // save session t.Lock() - _, ok := t.sockets[id+session] + _, ok := t.sessions[channel+sessionId] if ok { - // socket already exists + // session already exists t.Unlock() return nil, false } - t.sockets[id+session] = s + t.sessions[channel+sessionId] = s t.Unlock() - // return socket + // return session return s, true } // TODO: use tunnel id as part of the session -func (t *tun) newSession() string { +func (t *tun) newSessionId() string { return uuid.New().String() } @@ -168,10 +149,10 @@ func (t *tun) monitor() { } } -// process outgoing messages sent by all local sockets +// process outgoing messages sent by all local sessions func (t *tun) process() { // manage the send buffer - // all pseudo sockets throw everything down this + // all pseudo sessions throw everything down this for { select { case msg := <-t.send: @@ -190,6 +171,9 @@ func (t *tun) process() { // set the tunnel id on the outgoing message newMsg.Header["Micro-Tunnel-Id"] = msg.id + // set the tunnel channel on the outgoing message + newMsg.Header["Micro-Tunnel-Channel"] = msg.channel + // set the session id newMsg.Header["Micro-Tunnel-Session"] = msg.session @@ -203,10 +187,14 @@ func (t *tun) process() { log.Debugf("No links to send to") } + var sent bool + var err error + for node, link := range t.links { // if the link is not connected skip it if !link.connected { log.Debugf("Link for node %s not connected", node) + err = errors.New("link not connected") continue } @@ -214,6 +202,7 @@ func (t *tun) process() { // this is where we explicitly set the link // in a message received via the listen method if len(msg.link) > 0 && link.id != msg.link { + err = errors.New("link not found") continue } @@ -221,25 +210,41 @@ func (t *tun) process() { // and the message is being sent outbound via // a dialled connection don't use this link if link.loopback && msg.outbound { + err = errors.New("link is loopback") continue } // if the message was being returned by the loopback listener // send it back up the loopback link only if msg.loopback && !link.loopback { + err = errors.New("link is not loopback") continue } // send the message via the current link log.Debugf("Sending %+v to %s", newMsg, node) - if err := link.Send(newMsg); err != nil { - log.Debugf("Tunnel error sending %+v to %s: %v", newMsg, node, err) + if errr := link.Send(newMsg); errr != nil { + log.Debugf("Tunnel error sending %+v to %s: %v", newMsg, node, errr) + err = errors.New(errr.Error()) delete(t.links, node) continue } + // is sent + sent = true } t.Unlock() + + var gerr error + if !sent { + gerr = err + } + + // return error non blocking + select { + case msg.errChan <- gerr: + default: + } case <-t.closed: return } @@ -267,17 +272,23 @@ func (t *tun) listen(link *link) { return } + // always ensure we have the correct auth token + // TODO: segment the tunnel based on token + // e.g use it as the basis + token := msg.Header["Micro-Tunnel-Token"] + if token != t.token { + log.Debugf("Tunnel link %s received invalid token %s", token) + return + } + switch msg.Header["Micro-Tunnel"] { case "connect": log.Debugf("Tunnel link %s received connect message", link.Remote()) - // check the Micro-Tunnel-Token - token, ok := msg.Header["Micro-Tunnel-Token"] - if !ok { - continue - } + + id := msg.Header["Micro-Tunnel-Id"] // are we connecting to ourselves? - if token == t.token { + if id == t.id { link.loopback = true loopback = true } @@ -318,76 +329,77 @@ func (t *tun) listen(link *link) { return } - // strip message header - delete(msg.Header, "Micro-Tunnel") - // the tunnel id id := msg.Header["Micro-Tunnel-Id"] - delete(msg.Header, "Micro-Tunnel-Id") - + // the tunnel channel + channel := msg.Header["Micro-Tunnel-Channel"] // the session id - session := msg.Header["Micro-Tunnel-Session"] - delete(msg.Header, "Micro-Tunnel-Session") + sessionId := msg.Header["Micro-Tunnel-Session"] - // strip token header - delete(msg.Header, "Micro-Tunnel-Token") + // strip tunnel message header + for k, _ := range msg.Header { + if strings.HasPrefix(k, "Micro-Tunnel") { + delete(msg.Header, k) + } + } // if the session id is blank there's nothing we can do // TODO: check this is the case, is there any reason // why we'd have a blank session? Is the tunnel // used for some other purpose? - if len(id) == 0 || len(session) == 0 { + if len(channel) == 0 || len(sessionId) == 0 { continue } - var s *socket + var s *session var exists bool // If its a loopback connection then we've enabled link direction // listening side is used for listening, the dialling side for dialling switch { case loopback: - s, exists = t.getSocket(id, "listener") + s, exists = t.getSession(channel, "listener") default: - // get the socket based on the tunnel id and session + // get the session based on the tunnel id and session // this could be something we dialed in which case // we have a session for it otherwise its a listener - s, exists = t.getSocket(id, session) + s, exists = t.getSession(channel, sessionId) if !exists { // try get it based on just the tunnel id // the assumption here is that a listener // has no session but its set a listener session - s, exists = t.getSocket(id, "listener") + s, exists = t.getSession(channel, "listener") } } - // bail if no socket has been found + // bail if no session has been found if !exists { - log.Debugf("Tunnel skipping no socket exists") + log.Debugf("Tunnel skipping no session exists") // drop it, we don't care about // messages we don't know about continue } - log.Debugf("Tunnel using socket %s %s", s.id, s.session) - // is the socket closed? + log.Debugf("Tunnel using session %s %s", s.channel, s.session) + + // is the session closed? select { case <-s.closed: // closed - delete(t.sockets, id) + delete(t.sessions, channel) continue default: // process } - // is the socket new? + // is the session new? select { - // if its new the socket is actually blocked waiting + // if its new the session is actually blocked waiting // for a connection. so we check if its waiting. case <-s.wait: // if its waiting e.g its new then we close it default: - // set remote address of the socket + // set remote address of the session s.remote = msg.Header["Remote"] close(s.wait) } @@ -401,10 +413,12 @@ func (t *tun) listen(link *link) { // construct the internal message imsg := &message{ id: id, - session: session, + channel: channel, + session: sessionId, data: tmsg, link: link.id, loopback: loopback, + errChan: make(chan error, 1), } // append to recv backlog @@ -431,6 +445,7 @@ func (t *tun) keepalive(link *link) { if err := link.Send(&transport.Message{ Header: map[string]string{ "Micro-Tunnel": "keepalive", + "Micro-Tunnel-Id": t.id, "Micro-Tunnel-Token": t.token, }, }); err != nil { @@ -458,6 +473,7 @@ func (t *tun) setupLink(node string) (*link, error) { if err := c.Send(&transport.Message{ Header: map[string]string{ "Micro-Tunnel": "connect", + "Micro-Tunnel-Id": t.id, "Micro-Tunnel-Token": t.token, }, }); err != nil { @@ -568,6 +584,7 @@ func (t *tun) close() error { link.Send(&transport.Message{ Header: map[string]string{ "Micro-Tunnel": "close", + "Micro-Tunnel-Id": t.id, "Micro-Tunnel-Token": t.token, }, }) @@ -603,10 +620,10 @@ func (t *tun) Close() error { case <-t.closed: return nil default: - // close all the sockets - for id, s := range t.sockets { + // close all the sessions + for id, s := range t.sessions { s.Close() - delete(t.sockets, id) + delete(t.sessions, id) } // close the connection close(t.closed) @@ -622,52 +639,50 @@ func (t *tun) Close() error { } // Dial an address -func (t *tun) Dial(addr string) (Conn, error) { - log.Debugf("Tunnel dialing %s", addr) - c, ok := t.newSocket(addr, t.newSession()) +func (t *tun) Dial(channel string) (Session, error) { + log.Debugf("Tunnel dialing %s", channel) + c, ok := t.newSession(channel, t.newSessionId()) if !ok { - return nil, errors.New("error dialing " + addr) + return nil, errors.New("error dialing " + channel) } // set remote - c.remote = addr + c.remote = channel // set local c.local = "local" - // outbound socket + // outbound session c.outbound = true return c, nil } // Accept a connection on the address -func (t *tun) Listen(addr string) (Listener, error) { - log.Debugf("Tunnel listening on %s", addr) - // create a new socket by hashing the address - c, ok := t.newSocket(addr, "listener") +func (t *tun) Listen(channel string) (Listener, error) { + log.Debugf("Tunnel listening on %s", channel) + // create a new session by hashing the address + c, ok := t.newSession(channel, "listener") if !ok { - return nil, errors.New("already listening on " + addr) + return nil, errors.New("already listening on " + channel) } // set remote. it will be replaced by the first message received c.remote = "remote" // set local - c.local = addr + c.local = channel tl := &tunListener{ - addr: addr, + channel: channel, // the accept channel - accept: make(chan *socket, 128), + accept: make(chan *session, 128), // the channel to close closed: make(chan bool), // tunnel closed channel tunClosed: t.closed, - // the connection - conn: c, - // the listener socket - socket: c, + // the listener session + session: c, } // this kicks off the internal message processor - // for the listener so it can create pseudo sockets + // for the listener so it can create pseudo sessions // per session if they do not exist or pass messages // to the existign sessions go tl.process() diff --git a/tunnel/link.go b/tunnel/link.go index 6b8f30aa..fbec2e6a 100644 --- a/tunnel/link.go +++ b/tunnel/link.go @@ -1,10 +1,34 @@ package tunnel import ( + "sync" + "time" + "github.com/google/uuid" "github.com/micro/go-micro/transport" ) +type link struct { + sync.RWMutex + + transport.Socket + // unique id of this link e.g uuid + // which we define for ourselves + id string + // whether its a loopback connection + // this flag is used by the transport listener + // which accepts inbound quic connections + loopback bool + // whether its actually connected + // dialled side sets it to connected + // after sending the message. the + // listener waits for the connect + connected bool + // the last time we received a keepalive + // on this link from the remote side + lastKeepAlive time.Time +} + func newLink(s transport.Socket) *link { return &link{ Socket: s, diff --git a/tunnel/listener.go b/tunnel/listener.go index 42aadfd1..d62b58de 100644 --- a/tunnel/listener.go +++ b/tunnel/listener.go @@ -8,37 +8,37 @@ import ( type tunListener struct { // address of the listener - addr string + channel string // the accept channel - accept chan *socket + accept chan *session // the channel to close closed chan bool // the tunnel closed channel tunClosed chan bool - // the connection - conn Conn - // the listener socket - socket *socket + // the listener session + session *session } func (t *tunListener) process() { // our connection map for session - conns := make(map[string]*socket) + conns := make(map[string]*session) for { select { case <-t.closed: return // receive a new message - case m := <-t.socket.recv: - // get a socket - sock, ok := conns[m.session] + case m := <-t.session.recv: + // get a session + sess, ok := conns[m.session] log.Debugf("Tunnel listener received id %s session %s exists: %t", m.id, m.session, ok) if !ok { - // create a new socket session - sock = &socket{ - // our tunnel id + // create a new session session + sess = &session{ + // the id of the remote side id: m.id, + // the channel + channel: m.channel, // the session id session: m.session, // is loopback conn @@ -50,35 +50,37 @@ func (t *tunListener) process() { // recv called by the acceptor recv: make(chan *message, 128), // use the internal send buffer - send: t.socket.send, + send: t.session.send, // wait wait: make(chan bool), + // error channel + errChan: make(chan error, 1), } - // save the socket - conns[m.session] = sock + // save the session + conns[m.session] = sess // send to accept chan select { case <-t.closed: return - case t.accept <- sock: + case t.accept <- sess: } } // send this to the accept chan select { - case <-sock.closed: + case <-sess.closed: delete(conns, m.session) - case sock.recv <- m: + case sess.recv <- m: log.Debugf("Tunnel listener sent to recv chan id %s session %s", m.id, m.session) } } } } -func (t *tunListener) Addr() string { - return t.addr +func (t *tunListener) Channel() string { + return t.channel } // Close closes tunnel listener @@ -93,9 +95,9 @@ func (t *tunListener) Close() error { } // Everytime accept is called we essentially block till we get a new connection -func (t *tunListener) Accept() (Conn, error) { +func (t *tunListener) Accept() (Session, error) { select { - // if the socket is closed return + // if the session is closed return case <-t.closed: return nil, io.EOF case <-t.tunClosed: diff --git a/tunnel/options.go b/tunnel/options.go index 99406b05..9d612173 100644 --- a/tunnel/options.go +++ b/tunnel/options.go @@ -9,6 +9,8 @@ import ( var ( // DefaultAddress is default tunnel bind address DefaultAddress = ":0" + // The shared default token + DefaultToken = "micro" ) type Option func(*Options) @@ -21,6 +23,8 @@ type Options struct { Address string // Nodes are remote nodes Nodes []string + // The shared auth token + Token string // Transport listens to incoming connections Transport transport.Transport } @@ -46,6 +50,13 @@ func Nodes(n ...string) Option { } } +// Token sets the shared token for auth +func Token(t string) Option { + return func(o *Options) { + o.Token = t + } +} + // Transport listens for incoming connections func Transport(t transport.Transport) Option { return func(o *Options) { @@ -58,6 +69,7 @@ func DefaultOptions() Options { return Options{ Id: uuid.New().String(), Address: DefaultAddress, + Token: DefaultToken, Transport: quic.NewTransport(), } } diff --git a/tunnel/socket.go b/tunnel/session.go similarity index 60% rename from tunnel/socket.go rename to tunnel/session.go index 789d9555..a4c779f3 100644 --- a/tunnel/socket.go +++ b/tunnel/session.go @@ -2,15 +2,18 @@ package tunnel import ( "errors" + "io" "github.com/micro/go-micro/transport" "github.com/micro/go-micro/util/log" ) -// socket is our pseudo socket for transport.Socket -type socket struct { - // socket id based on Micro-Tunnel +// session is our pseudo session for transport.Socket +type session struct { + // unique id based on the remote tunnel id id string + // the channel name + channel string // the session id based on Micro.Tunnel-Session session string // closed @@ -25,12 +28,14 @@ type socket struct { recv chan *message // wait until we have a connection wait chan bool - // outbound marks the socket as outbound dialled connection + // outbound marks the session as outbound dialled connection outbound bool - // lookback marks the socket as a loopback on the inbound + // lookback marks the session as a loopback on the inbound loopback bool // the link on which this message was received link string + // the error response + errChan chan error } // message is sent over the send channel @@ -39,6 +44,8 @@ type message struct { typ string // tunnel id id string + // channel name + channel string // the session id session string // outbound marks the message as outbound @@ -49,28 +56,30 @@ type message struct { link string // transport data data *transport.Message + // the error channel + errChan chan error } -func (s *socket) Remote() string { +func (s *session) Remote() string { return s.remote } -func (s *socket) Local() string { +func (s *session) Local() string { return s.local } -func (s *socket) Id() string { - return s.id -} - -func (s *socket) Session() string { +func (s *session) Id() string { return s.session } -func (s *socket) Send(m *transport.Message) error { +func (s *session) Channel() string { + return s.channel +} + +func (s *session) Send(m *transport.Message) error { select { case <-s.closed: - return errors.New("socket is closed") + return errors.New("session is closed") default: // no op } @@ -89,28 +98,48 @@ func (s *socket) Send(m *transport.Message) error { msg := &message{ typ: "message", id: s.id, + channel: s.channel, session: s.session, outbound: s.outbound, loopback: s.loopback, data: data, // specify the link on which to send this - // it will be blank for dialled sockets + // it will be blank for dialled sessions link: s.link, + // error chan + errChan: s.errChan, } log.Debugf("Appending %+v to send backlog", msg) s.send <- msg + + // wait for an error response + select { + case err := <-msg.errChan: + return err + case <-s.closed: + return io.EOF + } + return nil } -func (s *socket) Recv(m *transport.Message) error { +func (s *session) Recv(m *transport.Message) error { select { case <-s.closed: - return errors.New("socket is closed") + return errors.New("session is closed") default: // no op } // recv from backlog msg := <-s.recv + + // check the error if one exists + select { + case err := <-msg.errChan: + return err + default: + } + log.Debugf("Received %+v from recv backlog", msg) // set message *m = *msg.data @@ -118,8 +147,8 @@ func (s *socket) Recv(m *transport.Message) error { return nil } -// Close closes the socket -func (s *socket) Close() error { +// Close closes the session +func (s *session) Close() error { select { case <-s.closed: // no op diff --git a/tunnel/transport/listener.go b/tunnel/transport/listener.go index b7a7280c..075f12cf 100644 --- a/tunnel/transport/listener.go +++ b/tunnel/transport/listener.go @@ -10,7 +10,7 @@ type tunListener struct { } func (t *tunListener) Addr() string { - return t.l.Addr() + return t.l.Channel() } func (t *tunListener) Close() error { diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index f7bc91cb..0349293a 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -17,27 +17,27 @@ type Tunnel interface { Connect() error // Close closes the tunnel Close() error - // Dial an endpoint - Dial(addr string) (Conn, error) - // Accept connections - Listen(addr string) (Listener, error) + // Connect to a channel + Dial(channel string) (Session, error) + // Accept connections on a channel + Listen(channel string) (Listener, error) // Name of the tunnel implementation String() string } // The listener provides similar constructs to the transport.Listener type Listener interface { - Addr() string + Channel() string Close() error - Accept() (Conn, error) + Accept() (Session, error) } -// Conn is a connection dialed or accepted which includes the tunnel id and session -type Conn interface { +// Session is a unique session created when dialling or accepting connections on the tunnel +type Session interface { // Specifies the tunnel id Id() string // The session - Session() string + Channel() string // a transport socket transport.Socket } From 0d94784e72337b02b3bf163d72483e0b46c37d09 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Sat, 31 Aug 2019 17:32:20 +0100 Subject: [PATCH 39/93] Add some tunnel comments --- tunnel/tunnel.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 0349293a..58c0ac27 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -5,8 +5,8 @@ import ( "github.com/micro/go-micro/transport" ) -// Tunnel creates a gre network tunnel on top of a link. -// It establishes multiple streams using the Micro-Tunnel-Id header +// Tunnel creates a gre tunnel on top of the go-micro/transport. +// It establishes multiple streams using the Micro-Tunnel-Channel header // and Micro-Tunnel-Session header. The tunnel id is a hash of // the address being requested. type Tunnel interface { @@ -27,16 +27,16 @@ type Tunnel interface { // The listener provides similar constructs to the transport.Listener type Listener interface { + Accept() (Session, error) Channel() string Close() error - Accept() (Session, error) } // Session is a unique session created when dialling or accepting connections on the tunnel type Session interface { - // Specifies the tunnel id + // The unique session id Id() string - // The session + // The channel name Channel() string // a transport socket transport.Socket From 52d9d75dfa840af6ae8079e8805d753b3cfd6a33 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Sat, 31 Aug 2019 18:26:48 +0100 Subject: [PATCH 40/93] use with stream for client connection --- client/rpc_client.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/client/rpc_client.go b/client/rpc_client.go index 754e4329..2fe51934 100644 --- a/client/rpc_client.go +++ b/client/rpc_client.go @@ -96,7 +96,15 @@ func (r *rpcClient) call(ctx context.Context, node *registry.Node, req Request, } } - c, err := r.pool.Get(address, transport.WithTimeout(opts.DialTimeout)) + dOpts := []transport.DialOption{ + transport.WithStream(), + } + + if opts.DialTimeout >= 0 { + dOpts = append(dOpts, transport.WithTimeout(opts.DialTimeout)) + } + + c, err := r.pool.Get(address, dOpts...) if err != nil { return errors.InternalServerError("go.micro.client", "connection error: %v", err) } From 2cdfed359f90c83ad9a958495547ff8c3105393e Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Mon, 2 Sep 2019 12:05:47 +0100 Subject: [PATCH 41/93] Separate lookup nodes and setup nodes --- tunnel/default.go | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/tunnel/default.go b/tunnel/default.go index 51468164..0b9b9136 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -132,17 +132,28 @@ func (t *tun) monitor() { case <-t.closed: return case <-reconnect.C: + var connect []string + + // build list of unknown nodes to connect to + t.RLock() for _, node := range t.options.Nodes { - t.Lock() if _, ok := t.links[node]; !ok { - link, err := t.setupLink(node) - if err != nil { - log.Debugf("Tunnel failed to setup node link to %s: %v", node, err) - t.Unlock() - continue - } - t.links[node] = link + connect = append(connect, node) } + } + t.RUnlock() + + for _, node := range connect { + // create new link + link, err := t.setupLink(node) + if err != nil { + log.Debugf("Tunnel failed to setup node link to %s: %v", node, err) + continue + } + + // save the link + t.Lock() + t.links[node] = link t.Unlock() } } From bf53c16e4bf20695e8cfedd893c5ec5f81f6aa5d Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Mon, 2 Sep 2019 11:42:45 +0100 Subject: [PATCH 42/93] Rough outline of Network introspection interface --- network/default.go | 11 +- network/handler/handler.go | 51 +++++++++ network/network.go | 2 + network/proto/network.micro.go | 16 +-- network/proto/network.pb.go | 189 ++++++++++++++++++++++++++++----- network/proto/network.proto | 24 ++++- 6 files changed, 255 insertions(+), 38 deletions(-) create mode 100644 network/handler/handler.go diff --git a/network/default.go b/network/default.go index 1b5669c6..564262e5 100644 --- a/network/default.go +++ b/network/default.go @@ -52,7 +52,7 @@ type network struct { // client is network client client client.Client - // tunClient is a mao of tunnel clients keyed over channel names + // tunClient is a map of tunnel clients keyed over tunnel channel names tunClient map[string]transport.Client sync.RWMutex @@ -118,6 +118,15 @@ func newNetwork(opts ...Option) Network { } } +// Options returns network options +func (n *network) Options() Options { + n.Lock() + options := n.options + n.Unlock() + + return options +} + // Name returns network name func (n *network) Name() string { return n.options.Name diff --git a/network/handler/handler.go b/network/handler/handler.go new file mode 100644 index 00000000..9fe1b3fb --- /dev/null +++ b/network/handler/handler.go @@ -0,0 +1,51 @@ +package handler + +import ( + "context" + + "github.com/micro/go-micro/errors" + "github.com/micro/go-micro/network" + pbNet "github.com/micro/go-micro/network/proto" + pbRtr "github.com/micro/go-micro/router/proto" +) + +// Network implements network handler +type Network struct { + Network network.Network +} + +// ListRoutes returns a list of routing table routes +func (n *Network) ListRoutes(ctx context.Context, req *pbRtr.Request, resp *pbRtr.ListResponse) error { + routes, err := n.Network.Options().Router.Table().List() + if err != nil { + return errors.InternalServerError("go.micro.network", "failed to list routes: %s", err) + } + + var respRoutes []*pbRtr.Route + for _, route := range routes { + respRoute := &pbRtr.Route{ + Service: route.Service, + Address: route.Address, + Gateway: route.Gateway, + Network: route.Network, + Router: route.Router, + Link: route.Link, + Metric: int64(route.Metric), + } + respRoutes = append(respRoutes, respRoute) + } + + resp.Routes = respRoutes + + return nil +} + +// ListNodes returns a list of all accessible nodes in the network +func (n *Network) ListNodes(ctx context.Context, req *pbNet.ListRequest, resp *pbNet.ListResponse) error { + return nil +} + +// ListNeighbours returns a list of immediate neighbours +func (n *Network) ListNeighbours(ctx context.Context, req *pbNet.NeighbourhoodRequest, resp *pbNet.NeighbourhoodResponse) error { + return nil +} diff --git a/network/network.go b/network/network.go index d9d10a55..1c810638 100644 --- a/network/network.go +++ b/network/network.go @@ -21,6 +21,8 @@ var ( // Network is micro network type Network interface { + // Options returns the network options + Options() Options // Name of the network Name() string // Address returns network bind address diff --git a/network/proto/network.micro.go b/network/proto/network.micro.go index fa0eefaf..c114ee96 100644 --- a/network/proto/network.micro.go +++ b/network/proto/network.micro.go @@ -37,7 +37,7 @@ var _ server.Option type NetworkService interface { ListRoutes(ctx context.Context, in *proto1.Request, opts ...client.CallOption) (*proto1.ListResponse, error) ListNodes(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) - ListNeighbours(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) + Neighbourhood(ctx context.Context, in *NeighbourhoodRequest, opts ...client.CallOption) (*NeighbourhoodResponse, error) } type networkService struct { @@ -78,9 +78,9 @@ func (c *networkService) ListNodes(ctx context.Context, in *ListRequest, opts .. return out, nil } -func (c *networkService) ListNeighbours(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) { - req := c.c.NewRequest(c.name, "Network.ListNeighbours", in) - out := new(ListResponse) +func (c *networkService) Neighbourhood(ctx context.Context, in *NeighbourhoodRequest, opts ...client.CallOption) (*NeighbourhoodResponse, error) { + req := c.c.NewRequest(c.name, "Network.Neighbourhood", in) + out := new(NeighbourhoodResponse) err := c.c.Call(ctx, req, out, opts...) if err != nil { return nil, err @@ -93,14 +93,14 @@ func (c *networkService) ListNeighbours(ctx context.Context, in *ListRequest, op type NetworkHandler interface { ListRoutes(context.Context, *proto1.Request, *proto1.ListResponse) error ListNodes(context.Context, *ListRequest, *ListResponse) error - ListNeighbours(context.Context, *ListRequest, *ListResponse) error + Neighbourhood(context.Context, *NeighbourhoodRequest, *NeighbourhoodResponse) error } func RegisterNetworkHandler(s server.Server, hdlr NetworkHandler, opts ...server.HandlerOption) error { type network interface { ListRoutes(ctx context.Context, in *proto1.Request, out *proto1.ListResponse) error ListNodes(ctx context.Context, in *ListRequest, out *ListResponse) error - ListNeighbours(ctx context.Context, in *ListRequest, out *ListResponse) error + Neighbourhood(ctx context.Context, in *NeighbourhoodRequest, out *NeighbourhoodResponse) error } type Network struct { network @@ -121,6 +121,6 @@ func (h *networkHandler) ListNodes(ctx context.Context, in *ListRequest, out *Li return h.NetworkHandler.ListNodes(ctx, in, out) } -func (h *networkHandler) ListNeighbours(ctx context.Context, in *ListRequest, out *ListResponse) error { - return h.NetworkHandler.ListNeighbours(ctx, in, out) +func (h *networkHandler) Neighbourhood(ctx context.Context, in *NeighbourhoodRequest, out *NeighbourhoodResponse) error { + return h.NetworkHandler.Neighbourhood(ctx, in, out) } diff --git a/network/proto/network.pb.go b/network/proto/network.pb.go index 9b5ccbaf..d7853f09 100644 --- a/network/proto/network.pb.go +++ b/network/proto/network.pb.go @@ -93,6 +93,136 @@ func (m *ListResponse) GetNodes() []*Node { return nil } +// NeighbourhoodRequest is sent to query node neighbourhood +type NeighbourhoodRequest struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *NeighbourhoodRequest) Reset() { *m = NeighbourhoodRequest{} } +func (m *NeighbourhoodRequest) String() string { return proto.CompactTextString(m) } +func (*NeighbourhoodRequest) ProtoMessage() {} +func (*NeighbourhoodRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_8571034d60397816, []int{2} +} + +func (m *NeighbourhoodRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_NeighbourhoodRequest.Unmarshal(m, b) +} +func (m *NeighbourhoodRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_NeighbourhoodRequest.Marshal(b, m, deterministic) +} +func (m *NeighbourhoodRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_NeighbourhoodRequest.Merge(m, src) +} +func (m *NeighbourhoodRequest) XXX_Size() int { + return xxx_messageInfo_NeighbourhoodRequest.Size(m) +} +func (m *NeighbourhoodRequest) XXX_DiscardUnknown() { + xxx_messageInfo_NeighbourhoodRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_NeighbourhoodRequest proto.InternalMessageInfo + +func (m *NeighbourhoodRequest) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +// NeighbourhoodResponse contains node neighbourhood hierarchy +type NeighbourhoodResponse struct { + Neighbourhood *Neighbour `protobuf:"bytes,1,opt,name=neighbourhood,proto3" json:"neighbourhood,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *NeighbourhoodResponse) Reset() { *m = NeighbourhoodResponse{} } +func (m *NeighbourhoodResponse) String() string { return proto.CompactTextString(m) } +func (*NeighbourhoodResponse) ProtoMessage() {} +func (*NeighbourhoodResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_8571034d60397816, []int{3} +} + +func (m *NeighbourhoodResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_NeighbourhoodResponse.Unmarshal(m, b) +} +func (m *NeighbourhoodResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_NeighbourhoodResponse.Marshal(b, m, deterministic) +} +func (m *NeighbourhoodResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_NeighbourhoodResponse.Merge(m, src) +} +func (m *NeighbourhoodResponse) XXX_Size() int { + return xxx_messageInfo_NeighbourhoodResponse.Size(m) +} +func (m *NeighbourhoodResponse) XXX_DiscardUnknown() { + xxx_messageInfo_NeighbourhoodResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_NeighbourhoodResponse proto.InternalMessageInfo + +func (m *NeighbourhoodResponse) GetNeighbourhood() *Neighbour { + if m != nil { + return m.Neighbourhood + } + return nil +} + +// Neighbourhood is node neighbourhood +type Neighbourhood struct { + // network node + Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` + // node neighbours + Neighbour []*Neighbour `protobuf:"bytes,2,rep,name=neighbour,proto3" json:"neighbour,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Neighbourhood) Reset() { *m = Neighbourhood{} } +func (m *Neighbourhood) String() string { return proto.CompactTextString(m) } +func (*Neighbourhood) ProtoMessage() {} +func (*Neighbourhood) Descriptor() ([]byte, []int) { + return fileDescriptor_8571034d60397816, []int{4} +} + +func (m *Neighbourhood) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Neighbourhood.Unmarshal(m, b) +} +func (m *Neighbourhood) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Neighbourhood.Marshal(b, m, deterministic) +} +func (m *Neighbourhood) XXX_Merge(src proto.Message) { + xxx_messageInfo_Neighbourhood.Merge(m, src) +} +func (m *Neighbourhood) XXX_Size() int { + return xxx_messageInfo_Neighbourhood.Size(m) +} +func (m *Neighbourhood) XXX_DiscardUnknown() { + xxx_messageInfo_Neighbourhood.DiscardUnknown(m) +} + +var xxx_messageInfo_Neighbourhood proto.InternalMessageInfo + +func (m *Neighbourhood) GetNode() *Node { + if m != nil { + return m.Node + } + return nil +} + +func (m *Neighbourhood) GetNeighbour() []*Neighbour { + if m != nil { + return m.Neighbour + } + return nil +} + // Node is network node type Node struct { // node ide @@ -108,7 +238,7 @@ func (m *Node) Reset() { *m = Node{} } func (m *Node) String() string { return proto.CompactTextString(m) } func (*Node) ProtoMessage() {} func (*Node) Descriptor() ([]byte, []int) { - return fileDescriptor_8571034d60397816, []int{2} + return fileDescriptor_8571034d60397816, []int{5} } func (m *Node) XXX_Unmarshal(b []byte) error { @@ -156,7 +286,7 @@ func (m *Connect) Reset() { *m = Connect{} } func (m *Connect) String() string { return proto.CompactTextString(m) } func (*Connect) ProtoMessage() {} func (*Connect) Descriptor() ([]byte, []int) { - return fileDescriptor_8571034d60397816, []int{3} + return fileDescriptor_8571034d60397816, []int{6} } func (m *Connect) XXX_Unmarshal(b []byte) error { @@ -186,7 +316,7 @@ func (m *Connect) GetNode() *Node { // Close is sent when the node disconnects from the network type Close struct { - // network mode + // network node Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -197,7 +327,7 @@ func (m *Close) Reset() { *m = Close{} } func (m *Close) String() string { return proto.CompactTextString(m) } func (*Close) ProtoMessage() {} func (*Close) Descriptor() ([]byte, []int) { - return fileDescriptor_8571034d60397816, []int{4} + return fileDescriptor_8571034d60397816, []int{7} } func (m *Close) XXX_Unmarshal(b []byte) error { @@ -227,7 +357,7 @@ func (m *Close) GetNode() *Node { // Neighbour is used to nnounce node neighbourhood type Neighbour struct { - // network mode + // network node Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` // neighbours Neighbours []*Node `protobuf:"bytes,3,rep,name=neighbours,proto3" json:"neighbours,omitempty"` @@ -240,7 +370,7 @@ func (m *Neighbour) Reset() { *m = Neighbour{} } func (m *Neighbour) String() string { return proto.CompactTextString(m) } func (*Neighbour) ProtoMessage() {} func (*Neighbour) Descriptor() ([]byte, []int) { - return fileDescriptor_8571034d60397816, []int{5} + return fileDescriptor_8571034d60397816, []int{8} } func (m *Neighbour) XXX_Unmarshal(b []byte) error { @@ -278,6 +408,9 @@ func (m *Neighbour) GetNeighbours() []*Node { func init() { proto.RegisterType((*ListRequest)(nil), "go.micro.network.ListRequest") proto.RegisterType((*ListResponse)(nil), "go.micro.network.ListResponse") + proto.RegisterType((*NeighbourhoodRequest)(nil), "go.micro.network.NeighbourhoodRequest") + proto.RegisterType((*NeighbourhoodResponse)(nil), "go.micro.network.NeighbourhoodResponse") + proto.RegisterType((*Neighbourhood)(nil), "go.micro.network.Neighbourhood") proto.RegisterType((*Node)(nil), "go.micro.network.Node") proto.RegisterType((*Connect)(nil), "go.micro.network.Connect") proto.RegisterType((*Close)(nil), "go.micro.network.Close") @@ -287,24 +420,28 @@ func init() { func init() { proto.RegisterFile("network.proto", fileDescriptor_8571034d60397816) } var fileDescriptor_8571034d60397816 = []byte{ - // 302 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x52, 0x5d, 0x4b, 0xc3, 0x30, - 0x14, 0xb5, 0xfb, 0x70, 0xec, 0xce, 0x0d, 0xc9, 0x83, 0x84, 0xc2, 0x64, 0xe4, 0x69, 0x88, 0xa6, - 0xb2, 0xa2, 0x4f, 0xbe, 0xed, 0xc1, 0x97, 0x51, 0xb0, 0xff, 0xc0, 0x36, 0xa1, 0x0b, 0xba, 0xdc, - 0x99, 0xa4, 0xf8, 0xc3, 0xfd, 0x03, 0xd2, 0xb4, 0x9b, 0xc5, 0x31, 0x61, 0xf8, 0x96, 0x73, 0xcf, - 0x3d, 0xe7, 0x5e, 0xee, 0x09, 0x8c, 0xb5, 0x74, 0x9f, 0x68, 0xde, 0xf8, 0xd6, 0xa0, 0x43, 0x72, - 0x59, 0x20, 0xdf, 0xa8, 0xdc, 0x20, 0x6f, 0xea, 0x61, 0x5c, 0x28, 0xb7, 0x2e, 0x33, 0x9e, 0xe3, - 0x26, 0xf2, 0x4c, 0x54, 0xe0, 0x5d, 0xfd, 0x30, 0x58, 0x3a, 0x69, 0x22, 0xaf, 0x6c, 0x40, 0x6d, - 0xc3, 0xc6, 0x30, 0x5a, 0x29, 0xeb, 0x52, 0xf9, 0x51, 0x4a, 0xeb, 0xd8, 0x13, 0x5c, 0xd4, 0xd0, - 0x6e, 0x51, 0x5b, 0x49, 0x6e, 0xa1, 0xaf, 0x51, 0x48, 0x4b, 0x83, 0x59, 0x77, 0x3e, 0x5a, 0x5c, - 0xf1, 0xdf, 0x53, 0x79, 0x82, 0x42, 0xa6, 0x75, 0x13, 0xbb, 0x87, 0x5e, 0x05, 0xc9, 0x04, 0x3a, - 0x4a, 0xd0, 0x60, 0x16, 0xcc, 0x87, 0x69, 0x47, 0x09, 0x42, 0x61, 0xf0, 0x2a, 0x84, 0x91, 0xd6, - 0xd2, 0x8e, 0x2f, 0xee, 0x20, 0x7b, 0x80, 0xc1, 0x12, 0xb5, 0x96, 0xb9, 0x23, 0x37, 0xd0, 0xab, - 0x5c, 0xbc, 0xec, 0xf8, 0x24, 0xdf, 0xc3, 0x62, 0xe8, 0x2f, 0xdf, 0xd1, 0xca, 0x93, 0x44, 0x08, - 0xc3, 0x44, 0xaa, 0x62, 0x9d, 0x61, 0x69, 0x4e, 0x11, 0x92, 0x47, 0x00, 0xbd, 0x13, 0x5a, 0xda, - 0xfd, 0xf3, 0x12, 0xad, 0xce, 0xc5, 0x57, 0x00, 0x83, 0xa4, 0x26, 0xc9, 0x33, 0x80, 0x3f, 0x6c, - 0x75, 0x7b, 0x4b, 0xe8, 0x8f, 0xba, 0x49, 0xa3, 0x09, 0x20, 0x9c, 0x1e, 0x30, 0xed, 0x3c, 0xd8, - 0x19, 0x59, 0xc1, 0xb0, 0xaa, 0x54, 0xc3, 0x2c, 0x99, 0x1e, 0x6e, 0xd1, 0x4a, 0x33, 0xbc, 0x3e, - 0x46, 0xef, 0xdd, 0x5e, 0x60, 0xe2, 0xdd, 0xf6, 0x4b, 0xff, 0xdb, 0x32, 0x3b, 0xf7, 0x1f, 0x2b, - 0xfe, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x19, 0x6e, 0xfe, 0x91, 0xb0, 0x02, 0x00, 0x00, + // 367 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0xc1, 0x4e, 0xc2, 0x40, + 0x10, 0x95, 0x02, 0x92, 0x0e, 0x62, 0xcc, 0x46, 0x4d, 0x53, 0x83, 0x21, 0x7b, 0x40, 0x62, 0xb4, + 0x18, 0x88, 0x26, 0x26, 0x5e, 0x0c, 0x07, 0x2f, 0x84, 0x43, 0x8f, 0xde, 0x2c, 0xbb, 0x29, 0x8d, + 0xd2, 0xc1, 0xdd, 0xad, 0xfe, 0x80, 0x1f, 0x6e, 0xba, 0x5d, 0x4a, 0x29, 0x82, 0xe1, 0xd6, 0x99, + 0x79, 0xf3, 0xde, 0xbc, 0xf6, 0x15, 0x5a, 0x31, 0x57, 0xdf, 0x28, 0xde, 0xbd, 0x85, 0x40, 0x85, + 0xe4, 0x24, 0x44, 0x6f, 0x1e, 0x4d, 0x05, 0x7a, 0xa6, 0xef, 0x0e, 0xc3, 0x48, 0xcd, 0x92, 0xc0, + 0x9b, 0xe2, 0xbc, 0xaf, 0x27, 0xfd, 0x10, 0x6f, 0xb3, 0x07, 0x81, 0x89, 0xe2, 0xa2, 0xaf, 0x37, + 0x4d, 0x91, 0xd1, 0xd0, 0x16, 0x34, 0xc7, 0x91, 0x54, 0x3e, 0xff, 0x4c, 0xb8, 0x54, 0xf4, 0x09, + 0x8e, 0xb2, 0x52, 0x2e, 0x30, 0x96, 0x9c, 0xdc, 0x40, 0x3d, 0x46, 0xc6, 0xa5, 0x53, 0xe9, 0x54, + 0x7b, 0xcd, 0xc1, 0xb9, 0x57, 0x56, 0xf5, 0x26, 0xc8, 0xb8, 0x9f, 0x81, 0x68, 0x17, 0x4e, 0x27, + 0x3c, 0x0a, 0x67, 0x01, 0x26, 0x62, 0x86, 0xc8, 0x0c, 0x2b, 0x39, 0x06, 0x2b, 0x62, 0x4e, 0xa5, + 0x53, 0xe9, 0xd9, 0xbe, 0x15, 0x31, 0xfa, 0x0a, 0x67, 0x25, 0x9c, 0x91, 0x7b, 0x4e, 0x5d, 0x16, + 0x06, 0x7a, 0xa7, 0x39, 0xb8, 0xf8, 0x43, 0x76, 0x09, 0xf3, 0xd7, 0x37, 0xe8, 0x17, 0xb4, 0xd6, + 0xb8, 0xc9, 0x35, 0xd4, 0xd2, 0xeb, 0x0c, 0xd5, 0x36, 0x07, 0x1a, 0x43, 0x1e, 0xc1, 0xce, 0xd9, + 0x1c, 0x4b, 0x5b, 0xde, 0xa9, 0xbd, 0x42, 0xd3, 0x3b, 0xa8, 0xa5, 0x44, 0x65, 0xaf, 0xc4, 0x81, + 0xc6, 0x1b, 0x63, 0x82, 0x4b, 0xe9, 0x58, 0xba, 0xb9, 0x2c, 0xe9, 0x3d, 0x34, 0x46, 0x18, 0xc7, + 0x7c, 0xaa, 0xf6, 0xb9, 0x91, 0x0e, 0xa1, 0x3e, 0xfa, 0x40, 0xc9, 0xf7, 0x5a, 0x42, 0xb0, 0xf3, + 0xab, 0xf7, 0x7a, 0x23, 0x0f, 0x00, 0xb9, 0x47, 0xe9, 0x54, 0x77, 0xa6, 0xa0, 0x80, 0x1c, 0xfc, + 0x58, 0xd0, 0x98, 0x64, 0x43, 0xf2, 0x02, 0xa0, 0x43, 0x95, 0xe6, 0x4e, 0x12, 0x67, 0xb5, 0x6d, + 0x92, 0x68, 0x62, 0xe2, 0xb6, 0x37, 0x26, 0xc5, 0x2c, 0xd2, 0x03, 0x32, 0x06, 0x3b, 0xed, 0xa4, + 0x62, 0x92, 0xb4, 0x37, 0xaf, 0x28, 0x24, 0xd9, 0xbd, 0xdc, 0x36, 0xce, 0xd9, 0x82, 0x72, 0x52, + 0xba, 0x3b, 0x3e, 0x75, 0x21, 0xce, 0xee, 0xd5, 0xbf, 0xb8, 0xa5, 0x46, 0x70, 0xa8, 0xff, 0xb2, + 0xe1, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0x4f, 0xab, 0x3f, 0xbd, 0x03, 0x00, 0x00, } diff --git a/network/proto/network.proto b/network/proto/network.proto index 2b3e3a2c..0fb854d6 100644 --- a/network/proto/network.proto +++ b/network/proto/network.proto @@ -8,7 +8,7 @@ import "github.com/micro/go-micro/router/proto/router.proto"; service Network { rpc ListRoutes(go.micro.router.Request) returns (go.micro.router.ListResponse) {}; rpc ListNodes(ListRequest) returns (ListResponse) {}; - rpc ListNeighbours(ListRequest) returns (ListResponse) {}; + rpc Neighbourhood(NeighbourhoodRequest) returns (NeighbourhoodResponse) {}; } // Empty request @@ -19,6 +19,24 @@ message ListResponse { repeated Node nodes = 1; } +// NeighbourhoodRequest is sent to query node neighbourhood +message NeighbourhoodRequest { + string id = 1; +} + +// NeighbourhoodResponse contains node neighbourhood hierarchy +message NeighbourhoodResponse { + Neighbour neighbourhood = 1; +} + +// Neighbourhood is node neighbourhood +message Neighbourhood { + // network node + Node node = 1; + // node neighbours + repeated Neighbour neighbour = 2; +} + // Node is network node message Node { // node ide @@ -35,13 +53,13 @@ message Connect { // Close is sent when the node disconnects from the network message Close { - // network mode + // network node Node node = 1; } // Neighbour is used to nnounce node neighbourhood message Neighbour { - // network mode + // network node Node node = 1; // neighbours repeated Node neighbours = 3; From 4f5a8492118ca0165b8c3f8373dfe79071a277a5 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Mon, 2 Sep 2019 12:39:26 +0100 Subject: [PATCH 43/93] Added Nodes method to Network interface --- network/default.go | 54 +++++++++++++++++++++++++++++++++++----------- network/network.go | 16 ++++++++++++-- 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/network/default.go b/network/default.go index 564262e5..9c0ae579 100644 --- a/network/default.go +++ b/network/default.go @@ -33,12 +33,31 @@ type node struct { id string // address is node address address string - // neighbours are node neightbours + // neighbours maps the node neighbourhood neighbours map[string]*node + // network returns network node is in + network Network +} + +// Id is node ide +func (n *node) Id() string { + return n.id +} + +// Address returns node address +func (n *node) Address() string { + return n.address +} + +// Network returns node network +func (n *node) Network() Network { + return n.network } // network implements Network interface type network struct { + // node is network node + *node // options configure the network options Options // rtr is network router @@ -60,8 +79,6 @@ type network struct { connected bool // closed closes the network closed chan bool - // neighbours maps the node neighbourhood - neighbours map[string]*node } // newNetwork returns a new network node @@ -106,16 +123,24 @@ func newNetwork(opts ...Option) Network { ), ) - return &network{ - options: options, - Router: options.Router, - Proxy: options.Proxy, - Tunnel: options.Tunnel, - server: server, - client: client, - tunClient: make(map[string]transport.Client), - neighbours: make(map[string]*node), + network := &network{ + node: &node{ + id: options.Id, + address: options.Address, + neighbours: make(map[string]*node), + }, + options: options, + Router: options.Router, + Proxy: options.Proxy, + Tunnel: options.Tunnel, + server: server, + client: client, + tunClient: make(map[string]transport.Client), } + + network.node.network = network + + return network } // Options returns network options @@ -695,6 +720,11 @@ func (n *network) Connect() error { return nil } +// Nodes returns a list of all network nodes +func (n *network) Nodes() []Node { + return nil +} + func (n *network) close() error { // stop the server if err := n.server.Stop(); err != nil { diff --git a/network/network.go b/network/network.go index 1c810638..df9c6045 100644 --- a/network/network.go +++ b/network/network.go @@ -21,14 +21,16 @@ var ( // Network is micro network type Network interface { + // Node is network node + Node // Options returns the network options Options() Options // Name of the network Name() string - // Address returns network bind address - Address() string // Connect starts the resolver and tunnel server Connect() error + // Nodes returns list of network nodes + Nodes() []Node // Close stops the tunnel and resolving Close() error // Client is micro client @@ -37,6 +39,16 @@ type Network interface { Server() server.Server } +// Node is network node +type Node interface { + // Id is node id + Id() string + // Address is node bind address + Address() string + // Network is the network node is in + Network() Network +} + // NewNetwork returns a new network interface func NewNetwork(opts ...Option) Network { return newNetwork(opts...) From 86665454e70b2e0dbbb95224b72bb10558a941a6 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Mon, 2 Sep 2019 17:06:21 +0100 Subject: [PATCH 44/93] Implementation of Nodes method. First take on full handler --- network/default.go | 47 +++++++++++++- network/handler/handler.go | 56 ++++++++++++++++- network/network.go | 22 ++++--- network/proto/network.pb.go | 118 ++++++++++-------------------------- network/proto/network.proto | 10 +-- 5 files changed, 146 insertions(+), 107 deletions(-) diff --git a/network/default.go b/network/default.go index 9c0ae579..7be7a68a 100644 --- a/network/default.go +++ b/network/default.go @@ -54,6 +54,16 @@ func (n *node) Network() Network { return n.network } +// Neighbourhood returns node neighbourhood +func (n *node) Neighbourhood() []Node { + var nodes []Node + for _, node := range n.neighbours { + nodes = append(nodes, node) + } + + return nodes +} + // network implements Network interface type network struct { // node is network node @@ -721,8 +731,43 @@ func (n *network) Connect() error { } // Nodes returns a list of all network nodes +// NOTE: this is a naive i.e. inefficient BFS implementation func (n *network) Nodes() []Node { - return nil + // map to track visited nodes + visited := make(map[string]*node) + // queue of the nodes to visit + queue := make([]*node, 1) + queue[0] = n.node + // add the root node to the map of the visited nodes + visited[n.node.id] = n.node + + for { + // pop a node from the queue + qnode := queue[0] + // pop is done by reslicing of the queue + // https://github.com/golang/go/wiki/SliceTricks + queue = queue[1:] + // iterate through all of its neighbours + // mark the visited nodes; enqueue the non-visted + for id, node := range qnode.neighbours { + if _, ok := visited[id]; !ok { + visited[id] = node + queue = append(queue, node) + } + } + // if no nodes are in the queue break + if len(queue) == 0 { + break + } + } + + nodes := make([]Node, 0) + // collecte all the nodes into slice + for _, node := range visited { + nodes = append(nodes, node) + } + + return nodes } func (n *network) close() error { diff --git a/network/handler/handler.go b/network/handler/handler.go index 9fe1b3fb..cd64dbac 100644 --- a/network/handler/handler.go +++ b/network/handler/handler.go @@ -2,6 +2,7 @@ package handler import ( "context" + "sort" "github.com/micro/go-micro/errors" "github.com/micro/go-micro/network" @@ -42,10 +43,61 @@ func (n *Network) ListRoutes(ctx context.Context, req *pbRtr.Request, resp *pbRt // ListNodes returns a list of all accessible nodes in the network func (n *Network) ListNodes(ctx context.Context, req *pbNet.ListRequest, resp *pbNet.ListResponse) error { + nodes := n.Network.Nodes() + + var respNodes []*pbNet.Node + for _, node := range nodes { + respNode := &pbNet.Node{ + Id: node.Id(), + Address: node.Address(), + } + respNodes = append(respNodes, respNode) + } + + resp.Nodes = respNodes + return nil } -// ListNeighbours returns a list of immediate neighbours -func (n *Network) ListNeighbours(ctx context.Context, req *pbNet.NeighbourhoodRequest, resp *pbNet.NeighbourhoodResponse) error { +// Neighbourhood returns a list of immediate neighbours +func (n *Network) Neighbourhood(ctx context.Context, req *pbNet.NeighbourhoodRequest, resp *pbNet.NeighbourhoodResponse) error { + // extract the id of the node to query + id := req.Id + // if no id is passed, we assume local node + if id == "" { + id = n.Network.Id() + } + + // get all the nodes in the network + nodes := n.Network.Nodes() + + var neighbours []*pbNet.Neighbour + // find a node with a given id + i := sort.Search(len(nodes), func(i int) bool { return nodes[i].Id() == id }) + // collect all the nodes in the neighbourhood of the found node + if i < len(nodes) && nodes[i].Id() == id { + for _, neighbour := range nodes[i].Neighbourhood() { + var nodeNeighbours []*pbNet.Node + for _, nodeNeighbour := range neighbour.Neighbourhood() { + nn := &pbNet.Node{ + Id: nodeNeighbour.Id(), + Address: nodeNeighbour.Address(), + } + nodeNeighbours = append(nodeNeighbours, nn) + } + // node is present at node[i] + neighbour := &pbNet.Neighbour{ + Node: &pbNet.Node{ + Id: neighbour.Id(), + Address: neighbour.Address(), + }, + Neighbours: nodeNeighbours, + } + neighbours = append(neighbours, neighbour) + } + } + + resp.Neighbours = neighbours + return nil } diff --git a/network/network.go b/network/network.go index df9c6045..f68cc147 100644 --- a/network/network.go +++ b/network/network.go @@ -19,6 +19,18 @@ var ( AnnounceTime = 30 * time.Second ) +// Node is network node +type Node interface { + // Id is node id + Id() string + // Address is node bind address + Address() string + // Neighbourhood is node neighbourhood + Neighbourhood() []Node + // Network is the network node is in + Network() Network +} + // Network is micro network type Network interface { // Node is network node @@ -39,16 +51,6 @@ type Network interface { Server() server.Server } -// Node is network node -type Node interface { - // Id is node id - Id() string - // Address is node bind address - Address() string - // Network is the network node is in - Network() Network -} - // NewNetwork returns a new network interface func NewNetwork(opts ...Option) Network { return newNetwork(opts...) diff --git a/network/proto/network.pb.go b/network/proto/network.pb.go index d7853f09..a6837679 100644 --- a/network/proto/network.pb.go +++ b/network/proto/network.pb.go @@ -135,10 +135,10 @@ func (m *NeighbourhoodRequest) GetId() string { // NeighbourhoodResponse contains node neighbourhood hierarchy type NeighbourhoodResponse struct { - Neighbourhood *Neighbour `protobuf:"bytes,1,opt,name=neighbourhood,proto3" json:"neighbourhood,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Neighbours []*Neighbour `protobuf:"bytes,1,rep,name=neighbours,proto3" json:"neighbours,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *NeighbourhoodResponse) Reset() { *m = NeighbourhoodResponse{} } @@ -166,59 +166,9 @@ func (m *NeighbourhoodResponse) XXX_DiscardUnknown() { var xxx_messageInfo_NeighbourhoodResponse proto.InternalMessageInfo -func (m *NeighbourhoodResponse) GetNeighbourhood() *Neighbour { +func (m *NeighbourhoodResponse) GetNeighbours() []*Neighbour { if m != nil { - return m.Neighbourhood - } - return nil -} - -// Neighbourhood is node neighbourhood -type Neighbourhood struct { - // network node - Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` - // node neighbours - Neighbour []*Neighbour `protobuf:"bytes,2,rep,name=neighbour,proto3" json:"neighbour,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Neighbourhood) Reset() { *m = Neighbourhood{} } -func (m *Neighbourhood) String() string { return proto.CompactTextString(m) } -func (*Neighbourhood) ProtoMessage() {} -func (*Neighbourhood) Descriptor() ([]byte, []int) { - return fileDescriptor_8571034d60397816, []int{4} -} - -func (m *Neighbourhood) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Neighbourhood.Unmarshal(m, b) -} -func (m *Neighbourhood) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Neighbourhood.Marshal(b, m, deterministic) -} -func (m *Neighbourhood) XXX_Merge(src proto.Message) { - xxx_messageInfo_Neighbourhood.Merge(m, src) -} -func (m *Neighbourhood) XXX_Size() int { - return xxx_messageInfo_Neighbourhood.Size(m) -} -func (m *Neighbourhood) XXX_DiscardUnknown() { - xxx_messageInfo_Neighbourhood.DiscardUnknown(m) -} - -var xxx_messageInfo_Neighbourhood proto.InternalMessageInfo - -func (m *Neighbourhood) GetNode() *Node { - if m != nil { - return m.Node - } - return nil -} - -func (m *Neighbourhood) GetNeighbour() []*Neighbour { - if m != nil { - return m.Neighbour + return m.Neighbours } return nil } @@ -238,7 +188,7 @@ func (m *Node) Reset() { *m = Node{} } func (m *Node) String() string { return proto.CompactTextString(m) } func (*Node) ProtoMessage() {} func (*Node) Descriptor() ([]byte, []int) { - return fileDescriptor_8571034d60397816, []int{5} + return fileDescriptor_8571034d60397816, []int{4} } func (m *Node) XXX_Unmarshal(b []byte) error { @@ -286,7 +236,7 @@ func (m *Connect) Reset() { *m = Connect{} } func (m *Connect) String() string { return proto.CompactTextString(m) } func (*Connect) ProtoMessage() {} func (*Connect) Descriptor() ([]byte, []int) { - return fileDescriptor_8571034d60397816, []int{6} + return fileDescriptor_8571034d60397816, []int{5} } func (m *Connect) XXX_Unmarshal(b []byte) error { @@ -327,7 +277,7 @@ func (m *Close) Reset() { *m = Close{} } func (m *Close) String() string { return proto.CompactTextString(m) } func (*Close) ProtoMessage() {} func (*Close) Descriptor() ([]byte, []int) { - return fileDescriptor_8571034d60397816, []int{7} + return fileDescriptor_8571034d60397816, []int{6} } func (m *Close) XXX_Unmarshal(b []byte) error { @@ -370,7 +320,7 @@ func (m *Neighbour) Reset() { *m = Neighbour{} } func (m *Neighbour) String() string { return proto.CompactTextString(m) } func (*Neighbour) ProtoMessage() {} func (*Neighbour) Descriptor() ([]byte, []int) { - return fileDescriptor_8571034d60397816, []int{8} + return fileDescriptor_8571034d60397816, []int{7} } func (m *Neighbour) XXX_Unmarshal(b []byte) error { @@ -410,7 +360,6 @@ func init() { proto.RegisterType((*ListResponse)(nil), "go.micro.network.ListResponse") proto.RegisterType((*NeighbourhoodRequest)(nil), "go.micro.network.NeighbourhoodRequest") proto.RegisterType((*NeighbourhoodResponse)(nil), "go.micro.network.NeighbourhoodResponse") - proto.RegisterType((*Neighbourhood)(nil), "go.micro.network.Neighbourhood") proto.RegisterType((*Node)(nil), "go.micro.network.Node") proto.RegisterType((*Connect)(nil), "go.micro.network.Connect") proto.RegisterType((*Close)(nil), "go.micro.network.Close") @@ -420,28 +369,27 @@ func init() { func init() { proto.RegisterFile("network.proto", fileDescriptor_8571034d60397816) } var fileDescriptor_8571034d60397816 = []byte{ - // 367 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0xc1, 0x4e, 0xc2, 0x40, - 0x10, 0x95, 0x02, 0x92, 0x0e, 0x62, 0xcc, 0x46, 0x4d, 0x53, 0x83, 0x21, 0x7b, 0x40, 0x62, 0xb4, - 0x18, 0x88, 0x26, 0x26, 0x5e, 0x0c, 0x07, 0x2f, 0x84, 0x43, 0x8f, 0xde, 0x2c, 0xbb, 0x29, 0x8d, - 0xd2, 0xc1, 0xdd, 0xad, 0xfe, 0x80, 0x1f, 0x6e, 0xba, 0x5d, 0x4a, 0x29, 0x82, 0xe1, 0xd6, 0x99, - 0x79, 0xf3, 0xde, 0xbc, 0xf6, 0x15, 0x5a, 0x31, 0x57, 0xdf, 0x28, 0xde, 0xbd, 0x85, 0x40, 0x85, - 0xe4, 0x24, 0x44, 0x6f, 0x1e, 0x4d, 0x05, 0x7a, 0xa6, 0xef, 0x0e, 0xc3, 0x48, 0xcd, 0x92, 0xc0, - 0x9b, 0xe2, 0xbc, 0xaf, 0x27, 0xfd, 0x10, 0x6f, 0xb3, 0x07, 0x81, 0x89, 0xe2, 0xa2, 0xaf, 0x37, - 0x4d, 0x91, 0xd1, 0xd0, 0x16, 0x34, 0xc7, 0x91, 0x54, 0x3e, 0xff, 0x4c, 0xb8, 0x54, 0xf4, 0x09, - 0x8e, 0xb2, 0x52, 0x2e, 0x30, 0x96, 0x9c, 0xdc, 0x40, 0x3d, 0x46, 0xc6, 0xa5, 0x53, 0xe9, 0x54, - 0x7b, 0xcd, 0xc1, 0xb9, 0x57, 0x56, 0xf5, 0x26, 0xc8, 0xb8, 0x9f, 0x81, 0x68, 0x17, 0x4e, 0x27, - 0x3c, 0x0a, 0x67, 0x01, 0x26, 0x62, 0x86, 0xc8, 0x0c, 0x2b, 0x39, 0x06, 0x2b, 0x62, 0x4e, 0xa5, - 0x53, 0xe9, 0xd9, 0xbe, 0x15, 0x31, 0xfa, 0x0a, 0x67, 0x25, 0x9c, 0x91, 0x7b, 0x4e, 0x5d, 0x16, - 0x06, 0x7a, 0xa7, 0x39, 0xb8, 0xf8, 0x43, 0x76, 0x09, 0xf3, 0xd7, 0x37, 0xe8, 0x17, 0xb4, 0xd6, - 0xb8, 0xc9, 0x35, 0xd4, 0xd2, 0xeb, 0x0c, 0xd5, 0x36, 0x07, 0x1a, 0x43, 0x1e, 0xc1, 0xce, 0xd9, - 0x1c, 0x4b, 0x5b, 0xde, 0xa9, 0xbd, 0x42, 0xd3, 0x3b, 0xa8, 0xa5, 0x44, 0x65, 0xaf, 0xc4, 0x81, - 0xc6, 0x1b, 0x63, 0x82, 0x4b, 0xe9, 0x58, 0xba, 0xb9, 0x2c, 0xe9, 0x3d, 0x34, 0x46, 0x18, 0xc7, - 0x7c, 0xaa, 0xf6, 0xb9, 0x91, 0x0e, 0xa1, 0x3e, 0xfa, 0x40, 0xc9, 0xf7, 0x5a, 0x42, 0xb0, 0xf3, - 0xab, 0xf7, 0x7a, 0x23, 0x0f, 0x00, 0xb9, 0x47, 0xe9, 0x54, 0x77, 0xa6, 0xa0, 0x80, 0x1c, 0xfc, - 0x58, 0xd0, 0x98, 0x64, 0x43, 0xf2, 0x02, 0xa0, 0x43, 0x95, 0xe6, 0x4e, 0x12, 0x67, 0xb5, 0x6d, - 0x92, 0x68, 0x62, 0xe2, 0xb6, 0x37, 0x26, 0xc5, 0x2c, 0xd2, 0x03, 0x32, 0x06, 0x3b, 0xed, 0xa4, - 0x62, 0x92, 0xb4, 0x37, 0xaf, 0x28, 0x24, 0xd9, 0xbd, 0xdc, 0x36, 0xce, 0xd9, 0x82, 0x72, 0x52, - 0xba, 0x3b, 0x3e, 0x75, 0x21, 0xce, 0xee, 0xd5, 0xbf, 0xb8, 0xa5, 0x46, 0x70, 0xa8, 0xff, 0xb2, - 0xe1, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0x4f, 0xab, 0x3f, 0xbd, 0x03, 0x00, 0x00, + // 344 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x52, 0x41, 0x4f, 0xf2, 0x40, + 0x10, 0xfd, 0x28, 0xf0, 0x11, 0x06, 0x31, 0x66, 0xa3, 0xa6, 0xa9, 0xc1, 0x90, 0x3d, 0x20, 0x31, + 0x5a, 0x0c, 0x44, 0x2f, 0x7a, 0xe3, 0xe0, 0x85, 0x70, 0x68, 0xfc, 0x03, 0x96, 0xdd, 0x94, 0x8d, + 0xb2, 0x83, 0xbb, 0xdb, 0xf8, 0x07, 0xfc, 0xe1, 0xa6, 0xdb, 0x2d, 0x16, 0x10, 0x0c, 0xb7, 0xce, + 0xcc, 0x7b, 0xf3, 0x66, 0xfb, 0x1e, 0xb4, 0x25, 0x37, 0x9f, 0xa8, 0xde, 0xc2, 0xa5, 0x42, 0x83, + 0xe4, 0x24, 0xc1, 0x70, 0x21, 0x66, 0x0a, 0x43, 0xd7, 0x0f, 0x46, 0x89, 0x30, 0xf3, 0x34, 0x0e, + 0x67, 0xb8, 0x18, 0xd8, 0xc9, 0x20, 0xc1, 0xdb, 0xfc, 0x43, 0x61, 0x6a, 0xb8, 0x1a, 0x58, 0xa6, + 0x2b, 0xf2, 0x35, 0xb4, 0x0d, 0xad, 0x89, 0xd0, 0x26, 0xe2, 0x1f, 0x29, 0xd7, 0x86, 0x3e, 0xc1, + 0x51, 0x5e, 0xea, 0x25, 0x4a, 0xcd, 0xc9, 0x0d, 0xd4, 0x25, 0x32, 0xae, 0xfd, 0x4a, 0xb7, 0xda, + 0x6f, 0x0d, 0xcf, 0xc3, 0x4d, 0xd5, 0x70, 0x8a, 0x8c, 0x47, 0x39, 0x88, 0xf6, 0xe0, 0x74, 0xca, + 0x45, 0x32, 0x8f, 0x31, 0x55, 0x73, 0x44, 0xe6, 0xb6, 0x92, 0x63, 0xf0, 0x04, 0xf3, 0x2b, 0xdd, + 0x4a, 0xbf, 0x19, 0x79, 0x82, 0xd1, 0x17, 0x38, 0xdb, 0xc0, 0x39, 0xb9, 0x47, 0x00, 0x59, 0x0c, + 0x0a, 0xcd, 0x8b, 0x5f, 0x34, 0x0b, 0x4c, 0x54, 0x82, 0xd3, 0x3b, 0xa8, 0x65, 0xc7, 0x6c, 0xaa, + 0x11, 0x1f, 0x1a, 0xaf, 0x8c, 0x29, 0xae, 0xb5, 0xef, 0xd9, 0x66, 0x51, 0xd2, 0x7b, 0x68, 0x8c, + 0x51, 0x4a, 0x3e, 0x33, 0xe4, 0x1a, 0x6a, 0xd9, 0x1b, 0x2c, 0x6d, 0xf7, 0x3b, 0x2d, 0x86, 0x8e, + 0xa0, 0x3e, 0x7e, 0x47, 0xcd, 0x0f, 0x22, 0x21, 0x34, 0x57, 0x67, 0x1f, 0x42, 0x24, 0x0f, 0x6b, + 0xff, 0xa4, 0xba, 0xd7, 0x87, 0x12, 0x72, 0xf8, 0xe5, 0x41, 0x63, 0x9a, 0x0f, 0xc9, 0x33, 0x80, + 0xb5, 0x35, 0x73, 0x5e, 0x13, 0xff, 0x87, 0xed, 0xb2, 0xe0, 0x8c, 0x0a, 0x3a, 0x5b, 0x93, 0x72, + 0x1a, 0xe8, 0x3f, 0x32, 0x81, 0x66, 0xd6, 0xc9, 0xc4, 0x34, 0xe9, 0x6c, 0x5f, 0x51, 0xca, 0x52, + 0x70, 0xb9, 0x6b, 0xbc, 0xda, 0x16, 0x43, 0x7b, 0x2d, 0x07, 0xa4, 0xb7, 0xc7, 0xeb, 0x52, 0xa0, + 0x82, 0xab, 0x3f, 0x71, 0x85, 0x46, 0xfc, 0xdf, 0xe6, 0x7c, 0xf4, 0x1d, 0x00, 0x00, 0xff, 0xff, + 0x11, 0x41, 0xd2, 0x02, 0x3f, 0x03, 0x00, 0x00, } diff --git a/network/proto/network.proto b/network/proto/network.proto index 0fb854d6..f540aafe 100644 --- a/network/proto/network.proto +++ b/network/proto/network.proto @@ -26,15 +26,7 @@ message NeighbourhoodRequest { // NeighbourhoodResponse contains node neighbourhood hierarchy message NeighbourhoodResponse { - Neighbour neighbourhood = 1; -} - -// Neighbourhood is node neighbourhood -message Neighbourhood { - // network node - Node node = 1; - // node neighbours - repeated Neighbour neighbour = 2; + repeated Neighbour neighbours = 1; } // Node is network node From a8d4299df94f7b378ece320d6930b50d813ad8d3 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Mon, 2 Sep 2019 20:00:52 +0100 Subject: [PATCH 45/93] Sort the returned slice of nodes before searching See docs: https://golang.org/pkg/sort/#Search --- network/handler/handler.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/network/handler/handler.go b/network/handler/handler.go index cd64dbac..67bca232 100644 --- a/network/handler/handler.go +++ b/network/handler/handler.go @@ -71,14 +71,21 @@ func (n *Network) Neighbourhood(ctx context.Context, req *pbNet.NeighbourhoodReq // get all the nodes in the network nodes := n.Network.Nodes() - var neighbours []*pbNet.Neighbour + // sort the slice of nodes + sort.Slice(nodes, func(i, j int) bool { return nodes[i].Id() <= nodes[j].Id() }) // find a node with a given id - i := sort.Search(len(nodes), func(i int) bool { return nodes[i].Id() == id }) + i := sort.Search(len(nodes), func(j int) bool { return nodes[j].Id() >= id }) + + var neighbours []*pbNet.Neighbour // collect all the nodes in the neighbourhood of the found node if i < len(nodes) && nodes[i].Id() == id { for _, neighbour := range nodes[i].Neighbourhood() { var nodeNeighbours []*pbNet.Node for _, nodeNeighbour := range neighbour.Neighbourhood() { + // don't return yourself in response + if nodeNeighbour.Id() == n.Network.Id() { + continue + } nn := &pbNet.Node{ Id: nodeNeighbour.Id(), Address: nodeNeighbour.Address(), From fb138779049beb9e3a27d8e3e69afe70b0101342 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Tue, 3 Sep 2019 02:58:17 +0100 Subject: [PATCH 46/93] Make Nodes() BFS implementation efficient --- network/default.go | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/network/default.go b/network/default.go index 7be7a68a..b0c77f62 100644 --- a/network/default.go +++ b/network/default.go @@ -1,6 +1,7 @@ package network import ( + "container/list" "sync" "time" @@ -731,38 +732,31 @@ func (n *network) Connect() error { } // Nodes returns a list of all network nodes -// NOTE: this is a naive i.e. inefficient BFS implementation func (n *network) Nodes() []Node { - // map to track visited nodes + //track the visited nodes visited := make(map[string]*node) // queue of the nodes to visit - queue := make([]*node, 1) - queue[0] = n.node - // add the root node to the map of the visited nodes + queue := list.New() + // push network node to the back of queue + queue.PushBack(n.node) + // mark the node as visited visited[n.node.id] = n.node - for { - // pop a node from the queue - qnode := queue[0] - // pop is done by reslicing of the queue - // https://github.com/golang/go/wiki/SliceTricks - queue = queue[1:] + // keep iterating over the queue until its empty + for qnode := queue.Front(); qnode != nil; qnode = qnode.Next() { + queue.Remove(qnode) // iterate through all of its neighbours // mark the visited nodes; enqueue the non-visted - for id, node := range qnode.neighbours { + for id, node := range qnode.Value.(*node).neighbours { if _, ok := visited[id]; !ok { visited[id] = node - queue = append(queue, node) + queue.PushBack(node) } } - // if no nodes are in the queue break - if len(queue) == 0 { - break - } } - nodes := make([]Node, 0) - // collecte all the nodes into slice + nodes := make([]Node, len(visited)) + // collect all the nodes and return them for _, node := range visited { nodes = append(nodes, node) } From ec6318befc5f02a602eb88d6cc028af004e92865 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Tue, 3 Sep 2019 10:00:14 +0100 Subject: [PATCH 47/93] Prune nodes that have not announced themselves for certain time period. --- network/default.go | 61 ++++++++++++++++++++++++++++++++++++++++++++-- network/network.go | 3 +++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/network/default.go b/network/default.go index b0c77f62..6e91c59c 100644 --- a/network/default.go +++ b/network/default.go @@ -36,8 +36,10 @@ type node struct { address string // neighbours maps the node neighbourhood neighbours map[string]*node - // network returns network node is in + // network returns the node network network Network + // lastSeen stores the time the node has been seen last time + lastSeen time.Time } // Id is node ide @@ -307,6 +309,7 @@ func (n *network) processNetChan(l tunnel.Listener) { id: pbNetNeighbour.Node.Id, address: pbNetNeighbour.Node.Address, neighbours: make(map[string]*node), + lastSeen: time.Now(), } n.Lock() // we override the existing neighbour map @@ -331,7 +334,10 @@ func (n *network) processNetChan(l tunnel.Listener) { continue } n.Lock() - delete(n.neighbours, pbNetClose.Node.Id) + if err := n.pruneNode(pbNetClose.Node.Id); err != nil { + log.Debugf("Network failed to prune the node %s: %v", pbNetClose.Node.Id, err) + continue + } n.Unlock() } case <-n.closed: @@ -393,6 +399,55 @@ func (n *network) announce(client transport.Client) { } } +// pruneNode removes a node with given id from the list of neighbours. It also removes all routes originted by this node. +// NOTE: this method is not thread-safe; when calling it make sure you lock the particular code segment +func (n *network) pruneNode(id string) error { + delete(n.neighbours, id) + // lookup all the routes originated at this node + q := router.NewQuery( + router.QueryRouter(id), + ) + routes, err := n.Router.Table().Query(q) + if err != nil && err != router.ErrRouteNotFound { + return err + } + // delete the found routes + for _, route := range routes { + if err := n.Router.Table().Delete(route); err != nil && err != router.ErrRouteNotFound { + return err + } + } + + return nil +} + +// prune the nodes that have not been seen for certain period of time defined by PruneTime +// Additionally, prune also removes all the routes originated by these nodes +func (n *network) prune() { + prune := time.NewTicker(PruneTime) + defer prune.Stop() + + for { + select { + case <-n.closed: + return + case <-prune.C: + n.Lock() + for id, node := range n.neighbours { + nodeAge := time.Since(node.lastSeen) + if nodeAge > PruneTime { + log.Debugf("Network deleting node %s: reached prune time threshold", id) + if err := n.pruneNode(id); err != nil { + log.Debugf("Network failed to prune the node %s: %v", id, err) + continue + } + } + } + n.Unlock() + } + } +} + // handleCtrlConn handles ControlChannel connections func (n *network) handleCtrlConn(sess tunnel.Session, msg chan *transport.Message) { for { @@ -695,6 +750,8 @@ func (n *network) Connect() error { go n.resolve() // broadcast neighbourhood go n.announce(netClient) + // prune stale nodes + go n.prune() // listen to network messages go n.processNetChan(netListener) // advertise service routes diff --git a/network/network.go b/network/network.go index f68cc147..5a9b7f6a 100644 --- a/network/network.go +++ b/network/network.go @@ -17,6 +17,9 @@ var ( ResolveTime = 1 * time.Minute // AnnounceTime defines time interval to periodically announce node neighbours AnnounceTime = 30 * time.Second + // PruneTime defines time interval to periodically check nodes that need to be pruned + // due to their not announcing their presence within this time interval + PruneTime = 90 * time.Second ) // Node is network node From 3ea4490d6c2ebc6c74dc56e404cf63206311632f Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Tue, 3 Sep 2019 15:02:30 +0100 Subject: [PATCH 48/93] Don't preallocate the slice if you don't index later on. --- network/default.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/default.go b/network/default.go index 6e91c59c..acac6cdc 100644 --- a/network/default.go +++ b/network/default.go @@ -812,7 +812,7 @@ func (n *network) Nodes() []Node { } } - nodes := make([]Node, len(visited)) + var nodes []Node // collect all the nodes and return them for _, node := range visited { nodes = append(nodes, node) From 6c7582a6beeb33ba57135ad0e6698469c0254583 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Tue, 3 Sep 2019 15:56:37 +0100 Subject: [PATCH 49/93] Move message to session --- tunnel/default.go | 29 +++++++++++++++-------------- tunnel/listener.go | 6 +++--- tunnel/session.go | 10 +++++----- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/tunnel/default.go b/tunnel/default.go index 0b9b9136..c7bd52ee 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -91,7 +91,7 @@ func (t *tun) getSession(channel, session string) (*session, bool) { func (t *tun) newSession(channel, sessionId string) (*session, bool) { // new session s := &session{ - id: t.id, + tunnel: t.id, channel: channel, session: sessionId, closed: make(chan bool), @@ -180,7 +180,7 @@ func (t *tun) process() { newMsg.Header["Micro-Tunnel"] = msg.typ // set the tunnel id on the outgoing message - newMsg.Header["Micro-Tunnel-Id"] = msg.id + newMsg.Header["Micro-Tunnel-Id"] = msg.tunnel // set the tunnel channel on the outgoing message newMsg.Header["Micro-Tunnel-Channel"] = msg.channel @@ -292,12 +292,19 @@ func (t *tun) listen(link *link) { return } - switch msg.Header["Micro-Tunnel"] { + // message type + mtype := msg.Header["Micro-Tunnel"] + // the tunnel id + id := msg.Header["Micro-Tunnel-Id"] + // the tunnel channel + channel := msg.Header["Micro-Tunnel-Channel"] + // the session id + sessionId := msg.Header["Micro-Tunnel-Session"] + + switch mtype { case "connect": log.Debugf("Tunnel link %s received connect message", link.Remote()) - id := msg.Header["Micro-Tunnel-Id"] - // are we connecting to ourselves? if id == t.id { link.loopback = true @@ -326,7 +333,7 @@ func (t *tun) listen(link *link) { link.lastKeepAlive = time.Now() t.Unlock() continue - case "message": + case "session": // process message log.Debugf("Received %+v from %s", msg, link.Remote()) default: @@ -340,13 +347,6 @@ func (t *tun) listen(link *link) { return } - // the tunnel id - id := msg.Header["Micro-Tunnel-Id"] - // the tunnel channel - channel := msg.Header["Micro-Tunnel-Channel"] - // the session id - sessionId := msg.Header["Micro-Tunnel-Session"] - // strip tunnel message header for k, _ := range msg.Header { if strings.HasPrefix(k, "Micro-Tunnel") { @@ -423,7 +423,8 @@ func (t *tun) listen(link *link) { // construct the internal message imsg := &message{ - id: id, + tunnel: id, + typ: mtype, channel: channel, session: sessionId, data: tmsg, diff --git a/tunnel/listener.go b/tunnel/listener.go index d62b58de..4bb8517a 100644 --- a/tunnel/listener.go +++ b/tunnel/listener.go @@ -31,12 +31,12 @@ func (t *tunListener) process() { case m := <-t.session.recv: // get a session sess, ok := conns[m.session] - log.Debugf("Tunnel listener received id %s session %s exists: %t", m.id, m.session, ok) + log.Debugf("Tunnel listener received channel %s session %s exists: %t", m.channel, m.session, ok) if !ok { // create a new session session sess = &session{ // the id of the remote side - id: m.id, + tunnel: m.tunnel, // the channel channel: m.channel, // the session id @@ -73,7 +73,7 @@ func (t *tunListener) process() { case <-sess.closed: delete(conns, m.session) case sess.recv <- m: - log.Debugf("Tunnel listener sent to recv chan id %s session %s", m.id, m.session) + log.Debugf("Tunnel listener sent to recv chan channel %s session %s", m.channel, m.session) } } } diff --git a/tunnel/session.go b/tunnel/session.go index a4c779f3..41616c7d 100644 --- a/tunnel/session.go +++ b/tunnel/session.go @@ -10,8 +10,8 @@ import ( // session is our pseudo session for transport.Socket type session struct { - // unique id based on the remote tunnel id - id string + // the tunnel id + tunnel string // the channel name channel string // the session id based on Micro.Tunnel-Session @@ -43,7 +43,7 @@ type message struct { // type of message typ string // tunnel id - id string + tunnel string // channel name channel string // the session id @@ -96,8 +96,8 @@ func (s *session) Send(m *transport.Message) error { // append to backlog msg := &message{ - typ: "message", - id: s.id, + typ: "session", + tunnel: s.tunnel, channel: s.channel, session: s.session, outbound: s.outbound, From 6eb6d050ed960f4c3703fdad938143f9a465312b Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Tue, 3 Sep 2019 16:39:27 +0100 Subject: [PATCH 50/93] Major bug overhaul in how we handle network.Nodes and related handler --- network/default.go | 48 ++++++++++++++++++++---------- network/handler/handler.go | 42 +++++++++++++-------------- network/proto/network.pb.go | 58 ++++++++++++++++++------------------- network/proto/network.proto | 2 +- 4 files changed, 84 insertions(+), 66 deletions(-) diff --git a/network/default.go b/network/default.go index acac6cdc..d9d8cbfe 100644 --- a/network/default.go +++ b/network/default.go @@ -30,6 +30,7 @@ var ( // node is network node type node struct { + sync.RWMutex // id is node id id string // address is node address @@ -60,9 +61,18 @@ func (n *node) Network() Network { // Neighbourhood returns node neighbourhood func (n *node) Neighbourhood() []Node { var nodes []Node - for _, node := range n.neighbours { - nodes = append(nodes, node) + n.RLock() + for _, neighbourNode := range n.neighbours { + // make a copy of the node + n := &node{ + id: neighbourNode.id, + address: neighbourNode.address, + network: neighbourNode.network, + } + // NOTE: we do not care about neighbour's neighbours + nodes = append(nodes, n) } + n.RUnlock() return nodes } @@ -287,13 +297,19 @@ func (n *network) processNetChan(l tunnel.Listener) { if pbNetConnect.Node.Id == n.options.Id { continue } - neighbour := &node{ + n.Lock() + // if the entry already exists skip adding it + if _, ok := n.neighbours[pbNetConnect.Node.Id]; ok { + n.Unlock() + continue + } + // add a new neighbour; + // NOTE: new node does not have any neighbours + n.neighbours[pbNetConnect.Node.Id] = &node{ id: pbNetConnect.Node.Id, address: pbNetConnect.Node.Address, neighbours: make(map[string]*node), } - n.Lock() - n.neighbours[neighbour.id] = neighbour n.Unlock() case "neighbour": pbNetNeighbour := &pbNet.Neighbour{} @@ -305,22 +321,24 @@ func (n *network) processNetChan(l tunnel.Listener) { if pbNetNeighbour.Node.Id == n.options.Id { continue } - neighbour := &node{ - id: pbNetNeighbour.Node.Id, - address: pbNetNeighbour.Node.Address, - neighbours: make(map[string]*node), - lastSeen: time.Now(), - } n.Lock() - // we override the existing neighbour map - n.neighbours[neighbour.id] = neighbour - // store the neighbouring node and its neighbours + // only add the neighbour if it's not already in the neighbourhood + if _, ok := n.neighbours[pbNetNeighbour.Node.Id]; !ok { + neighbour := &node{ + id: pbNetNeighbour.Node.Id, + address: pbNetNeighbour.Node.Address, + neighbours: make(map[string]*node), + lastSeen: time.Now(), + } + n.neighbours[pbNetNeighbour.Node.Id] = neighbour + } + // update/store the neighbour node neighbours for _, pbNeighbour := range pbNetNeighbour.Neighbours { neighbourNode := &node{ id: pbNeighbour.Id, address: pbNeighbour.Address, } - n.neighbours[neighbour.id].neighbours[neighbourNode.id] = neighbourNode + n.neighbours[pbNetNeighbour.Node.Id].neighbours[neighbourNode.id] = neighbourNode } n.Unlock() case "close": diff --git a/network/handler/handler.go b/network/handler/handler.go index 67bca232..3740d4a9 100644 --- a/network/handler/handler.go +++ b/network/handler/handler.go @@ -76,35 +76,35 @@ func (n *Network) Neighbourhood(ctx context.Context, req *pbNet.NeighbourhoodReq // find a node with a given id i := sort.Search(len(nodes), func(j int) bool { return nodes[j].Id() >= id }) - var neighbours []*pbNet.Neighbour + var neighbours []*pbNet.Node // collect all the nodes in the neighbourhood of the found node if i < len(nodes) && nodes[i].Id() == id { for _, neighbour := range nodes[i].Neighbourhood() { - var nodeNeighbours []*pbNet.Node - for _, nodeNeighbour := range neighbour.Neighbourhood() { - // don't return yourself in response - if nodeNeighbour.Id() == n.Network.Id() { - continue - } - nn := &pbNet.Node{ - Id: nodeNeighbour.Id(), - Address: nodeNeighbour.Address(), - } - nodeNeighbours = append(nodeNeighbours, nn) + // don't return yourself in response + if neighbour.Id() == n.Network.Id() { + continue } - // node is present at node[i] - neighbour := &pbNet.Neighbour{ - Node: &pbNet.Node{ - Id: neighbour.Id(), - Address: neighbour.Address(), - }, - Neighbours: nodeNeighbours, + pbNeighbour := &pbNet.Node{ + Id: neighbour.Id(), + Address: neighbour.Address(), } - neighbours = append(neighbours, neighbour) + neighbours = append(neighbours, pbNeighbour) } } - resp.Neighbours = neighbours + // requested neighbourhood node + node := &pbNet.Node{ + Id: nodes[i].Id(), + Address: nodes[i].Address(), + } + + // creaate neighbourhood answer + neighbourhood := &pbNet.Neighbour{ + Node: node, + Neighbours: neighbours, + } + + resp.Neighbourhood = neighbourhood return nil } diff --git a/network/proto/network.pb.go b/network/proto/network.pb.go index a6837679..74124324 100644 --- a/network/proto/network.pb.go +++ b/network/proto/network.pb.go @@ -135,10 +135,10 @@ func (m *NeighbourhoodRequest) GetId() string { // NeighbourhoodResponse contains node neighbourhood hierarchy type NeighbourhoodResponse struct { - Neighbours []*Neighbour `protobuf:"bytes,1,rep,name=neighbours,proto3" json:"neighbours,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Neighbourhood *Neighbour `protobuf:"bytes,1,opt,name=neighbourhood,proto3" json:"neighbourhood,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *NeighbourhoodResponse) Reset() { *m = NeighbourhoodResponse{} } @@ -166,9 +166,9 @@ func (m *NeighbourhoodResponse) XXX_DiscardUnknown() { var xxx_messageInfo_NeighbourhoodResponse proto.InternalMessageInfo -func (m *NeighbourhoodResponse) GetNeighbours() []*Neighbour { +func (m *NeighbourhoodResponse) GetNeighbourhood() *Neighbour { if m != nil { - return m.Neighbours + return m.Neighbourhood } return nil } @@ -369,27 +369,27 @@ func init() { func init() { proto.RegisterFile("network.proto", fileDescriptor_8571034d60397816) } var fileDescriptor_8571034d60397816 = []byte{ - // 344 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x52, 0x41, 0x4f, 0xf2, 0x40, - 0x10, 0xfd, 0x28, 0xf0, 0x11, 0x06, 0x31, 0x66, 0xa3, 0xa6, 0xa9, 0xc1, 0x90, 0x3d, 0x20, 0x31, - 0x5a, 0x0c, 0x44, 0x2f, 0x7a, 0xe3, 0xe0, 0x85, 0x70, 0x68, 0xfc, 0x03, 0x96, 0xdd, 0x94, 0x8d, - 0xb2, 0x83, 0xbb, 0xdb, 0xf8, 0x07, 0xfc, 0xe1, 0xa6, 0xdb, 0x2d, 0x16, 0x10, 0x0c, 0xb7, 0xce, - 0xcc, 0x7b, 0xf3, 0x66, 0xfb, 0x1e, 0xb4, 0x25, 0x37, 0x9f, 0xa8, 0xde, 0xc2, 0xa5, 0x42, 0x83, - 0xe4, 0x24, 0xc1, 0x70, 0x21, 0x66, 0x0a, 0x43, 0xd7, 0x0f, 0x46, 0x89, 0x30, 0xf3, 0x34, 0x0e, - 0x67, 0xb8, 0x18, 0xd8, 0xc9, 0x20, 0xc1, 0xdb, 0xfc, 0x43, 0x61, 0x6a, 0xb8, 0x1a, 0x58, 0xa6, - 0x2b, 0xf2, 0x35, 0xb4, 0x0d, 0xad, 0x89, 0xd0, 0x26, 0xe2, 0x1f, 0x29, 0xd7, 0x86, 0x3e, 0xc1, - 0x51, 0x5e, 0xea, 0x25, 0x4a, 0xcd, 0xc9, 0x0d, 0xd4, 0x25, 0x32, 0xae, 0xfd, 0x4a, 0xb7, 0xda, - 0x6f, 0x0d, 0xcf, 0xc3, 0x4d, 0xd5, 0x70, 0x8a, 0x8c, 0x47, 0x39, 0x88, 0xf6, 0xe0, 0x74, 0xca, - 0x45, 0x32, 0x8f, 0x31, 0x55, 0x73, 0x44, 0xe6, 0xb6, 0x92, 0x63, 0xf0, 0x04, 0xf3, 0x2b, 0xdd, - 0x4a, 0xbf, 0x19, 0x79, 0x82, 0xd1, 0x17, 0x38, 0xdb, 0xc0, 0x39, 0xb9, 0x47, 0x00, 0x59, 0x0c, - 0x0a, 0xcd, 0x8b, 0x5f, 0x34, 0x0b, 0x4c, 0x54, 0x82, 0xd3, 0x3b, 0xa8, 0x65, 0xc7, 0x6c, 0xaa, - 0x11, 0x1f, 0x1a, 0xaf, 0x8c, 0x29, 0xae, 0xb5, 0xef, 0xd9, 0x66, 0x51, 0xd2, 0x7b, 0x68, 0x8c, - 0x51, 0x4a, 0x3e, 0x33, 0xe4, 0x1a, 0x6a, 0xd9, 0x1b, 0x2c, 0x6d, 0xf7, 0x3b, 0x2d, 0x86, 0x8e, - 0xa0, 0x3e, 0x7e, 0x47, 0xcd, 0x0f, 0x22, 0x21, 0x34, 0x57, 0x67, 0x1f, 0x42, 0x24, 0x0f, 0x6b, - 0xff, 0xa4, 0xba, 0xd7, 0x87, 0x12, 0x72, 0xf8, 0xe5, 0x41, 0x63, 0x9a, 0x0f, 0xc9, 0x33, 0x80, - 0xb5, 0x35, 0x73, 0x5e, 0x13, 0xff, 0x87, 0xed, 0xb2, 0xe0, 0x8c, 0x0a, 0x3a, 0x5b, 0x93, 0x72, - 0x1a, 0xe8, 0x3f, 0x32, 0x81, 0x66, 0xd6, 0xc9, 0xc4, 0x34, 0xe9, 0x6c, 0x5f, 0x51, 0xca, 0x52, - 0x70, 0xb9, 0x6b, 0xbc, 0xda, 0x16, 0x43, 0x7b, 0x2d, 0x07, 0xa4, 0xb7, 0xc7, 0xeb, 0x52, 0xa0, - 0x82, 0xab, 0x3f, 0x71, 0x85, 0x46, 0xfc, 0xdf, 0xe6, 0x7c, 0xf4, 0x1d, 0x00, 0x00, 0xff, 0xff, - 0x11, 0x41, 0xd2, 0x02, 0x3f, 0x03, 0x00, 0x00, + // 348 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x52, 0x41, 0x4f, 0x32, 0x31, + 0x10, 0xfd, 0x58, 0xe0, 0x23, 0x0c, 0x62, 0x4c, 0xa3, 0x66, 0xb3, 0x06, 0x43, 0x7a, 0x40, 0x62, + 0x74, 0x31, 0x10, 0x3d, 0x79, 0x31, 0x1c, 0xbc, 0x10, 0x0e, 0x7b, 0xf4, 0xe6, 0xd2, 0x66, 0x69, + 0x94, 0x1d, 0x6c, 0xbb, 0xf1, 0x0f, 0xf8, 0xc3, 0x4d, 0xbb, 0x05, 0x97, 0x45, 0x30, 0xdc, 0xda, + 0x99, 0xf7, 0xe6, 0xcd, 0xe4, 0x3d, 0x68, 0xa7, 0x5c, 0x7f, 0xa2, 0x7c, 0x0b, 0x97, 0x12, 0x35, + 0x92, 0x93, 0x04, 0xc3, 0x85, 0x98, 0x49, 0x0c, 0x5d, 0x3d, 0x18, 0x25, 0x42, 0xcf, 0xb3, 0x38, + 0x9c, 0xe1, 0x62, 0x60, 0x3b, 0x83, 0x04, 0x6f, 0xf3, 0x87, 0xc4, 0x4c, 0x73, 0x39, 0xb0, 0x4c, + 0xf7, 0xc9, 0xc7, 0xd0, 0x36, 0xb4, 0x26, 0x42, 0xe9, 0x88, 0x7f, 0x64, 0x5c, 0x69, 0xfa, 0x08, + 0x47, 0xf9, 0x57, 0x2d, 0x31, 0x55, 0x9c, 0xdc, 0x40, 0x3d, 0x45, 0xc6, 0x95, 0x5f, 0xe9, 0x56, + 0xfb, 0xad, 0xe1, 0x79, 0x58, 0x56, 0x0d, 0xa7, 0xc8, 0x78, 0x94, 0x83, 0x68, 0x0f, 0x4e, 0xa7, + 0x5c, 0x24, 0xf3, 0x18, 0x33, 0x39, 0x47, 0x64, 0x6e, 0x2a, 0x39, 0x06, 0x4f, 0x30, 0xbf, 0xd2, + 0xad, 0xf4, 0x9b, 0x91, 0x27, 0x18, 0x7d, 0x81, 0xb3, 0x12, 0xce, 0xc9, 0x3d, 0x99, 0x2b, 0x0b, + 0x0d, 0xcb, 0x69, 0x0d, 0x2f, 0x7e, 0x91, 0x5d, 0xc1, 0xa2, 0x4d, 0x06, 0xbd, 0x83, 0x9a, 0x59, + 0xa9, 0xac, 0x49, 0x7c, 0x68, 0xbc, 0x32, 0x26, 0xb9, 0x52, 0xbe, 0x67, 0x8b, 0xab, 0x2f, 0xbd, + 0x87, 0xc6, 0x18, 0xd3, 0x94, 0xcf, 0x34, 0xb9, 0x86, 0x9a, 0xb9, 0xc4, 0xc9, 0xee, 0xba, 0xd6, + 0x62, 0xe8, 0x08, 0xea, 0xe3, 0x77, 0x54, 0xfc, 0x20, 0x12, 0x42, 0x73, 0xbd, 0xf9, 0x21, 0x44, + 0xf2, 0x00, 0xb0, 0xbe, 0x53, 0xf9, 0xd5, 0xbd, 0x6e, 0x14, 0x90, 0xc3, 0x2f, 0x0f, 0x1a, 0xd3, + 0xbc, 0x49, 0x9e, 0x01, 0xac, 0xb9, 0xc6, 0x7f, 0x45, 0xfc, 0x1f, 0xb6, 0x4b, 0x84, 0xb3, 0x2b, + 0xe8, 0x6c, 0x75, 0x8a, 0x99, 0xa0, 0xff, 0xc8, 0x04, 0x9a, 0xa6, 0x62, 0xc4, 0x14, 0xe9, 0x6c, + 0x6f, 0x51, 0x48, 0x54, 0x70, 0xb9, 0xab, 0xbd, 0x9e, 0x16, 0x43, 0x7b, 0x23, 0x0d, 0xa4, 0xb7, + 0xc7, 0xee, 0x42, 0xac, 0x82, 0xab, 0x3f, 0x71, 0x2b, 0x8d, 0xf8, 0xbf, 0x4d, 0xfb, 0xe8, 0x3b, + 0x00, 0x00, 0xff, 0xff, 0xfb, 0xa1, 0x6b, 0xb0, 0x45, 0x03, 0x00, 0x00, } diff --git a/network/proto/network.proto b/network/proto/network.proto index f540aafe..cfba6d04 100644 --- a/network/proto/network.proto +++ b/network/proto/network.proto @@ -26,7 +26,7 @@ message NeighbourhoodRequest { // NeighbourhoodResponse contains node neighbourhood hierarchy message NeighbourhoodResponse { - repeated Neighbour neighbours = 1; + Neighbour neighbourhood = 1; } // Node is network node From 4f4b3d3bae41f9f9f4e90871ff6369c2256da47b Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Tue, 3 Sep 2019 19:51:52 +0100 Subject: [PATCH 51/93] Send connect message to NetworkChannel once we are not at caller mercy --- network/default.go | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/network/default.go b/network/default.go index d9d8cbfe..202e18f1 100644 --- a/network/default.go +++ b/network/default.go @@ -764,23 +764,10 @@ func (n *network) Connect() error { return err } - // go resolving network nodes - go n.resolve() - // broadcast neighbourhood - go n.announce(netClient) - // prune stale nodes - go n.prune() - // listen to network messages - go n.processNetChan(netListener) - // advertise service routes - go n.advertise(ctrlClient, advertChan) - // accept and process routes - go n.processCtrlChan(ctrlListener) - - // set connected to true - n.connected = true - // send connect message to NetworkChannel + // NOTE: in theory we could do this as soon as + // Dial to NetworkChannel succeeds, but instead + // we initialize all other node resources first node := &pbNet.Node{ Id: n.options.Id, Address: n.options.Address, @@ -803,6 +790,22 @@ func (n *network) Connect() error { } } + // go resolving network nodes + go n.resolve() + // broadcast neighbourhood + go n.announce(netClient) + // prune stale nodes + go n.prune() + // listen to network messages + go n.processNetChan(netListener) + // advertise service routes + go n.advertise(ctrlClient, advertChan) + // accept and process routes + go n.processCtrlChan(ctrlListener) + + // set connected to true + n.connected = true + return nil } From bb64f9431382c9056b1a423a3fd5b0ed6db409be Mon Sep 17 00:00:00 2001 From: Yumin Wu Date: Wed, 4 Sep 2019 15:47:46 +0800 Subject: [PATCH 52/93] .gitignore file for develop tools --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 608e9d0d..500d68ca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ +# Develop tools +/.vscode/ +/.idea/ + # Binaries for programs and plugins *.exe *.exe~ From 5b991cd2c2d06bfde25136daa0b75c06a996e1da Mon Sep 17 00:00:00 2001 From: Yumin Wu Date: Wed, 4 Sep 2019 15:49:58 +0800 Subject: [PATCH 53/93] Update config source README file --- config/source/env/README.md | 2 +- config/source/flag/README.md | 2 +- config/source/memory/README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/source/env/README.md b/config/source/env/README.md index 25bfa07a..61a18f8d 100644 --- a/config/source/env/README.md +++ b/config/source/env/README.md @@ -91,6 +91,6 @@ Load the source into config // Create new config conf := config.NewConfig() -// Load file source +// Load env source conf.Load(src) ``` diff --git a/config/source/flag/README.md b/config/source/flag/README.md index fb78bae2..79e5cb54 100644 --- a/config/source/flag/README.md +++ b/config/source/flag/README.md @@ -42,6 +42,6 @@ Load the source into config // Create new config conf := config.NewConfig() -// Load file source +// Load flag source conf.Load(flagSource) ``` diff --git a/config/source/memory/README.md b/config/source/memory/README.md index 7f1a4008..adef980b 100644 --- a/config/source/memory/README.md +++ b/config/source/memory/README.md @@ -39,6 +39,6 @@ Load the source into config // Create new config conf := config.NewConfig() -// Load file source +// Load memory source conf.Load(memorySource) ``` From b9c437fbfeb2e2e425b2287c7f479b8fe5c013eb Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 4 Sep 2019 09:48:05 +0100 Subject: [PATCH 54/93] Tunnel discover/announce/open/session/close --- network/default.go | 4 +- tunnel/default.go | 262 ++++++++++++++++++++++++++++++++++++++++----- tunnel/link.go | 65 ++++++++++- tunnel/listener.go | 96 ++++++++++++++++- tunnel/options.go | 26 +++++ tunnel/session.go | 144 +++++++++++++++++++++++-- tunnel/tunnel.go | 11 +- 7 files changed, 565 insertions(+), 43 deletions(-) diff --git a/network/default.go b/network/default.go index d9d8cbfe..e7b8c781 100644 --- a/network/default.go +++ b/network/default.go @@ -718,7 +718,7 @@ func (n *network) Connect() error { ) // dial into ControlChannel to send route adverts - ctrlClient, err := n.Tunnel.Dial(ControlChannel) + ctrlClient, err := n.Tunnel.Dial(ControlChannel, tunnel.DialMulticast()) if err != nil { return err } @@ -732,7 +732,7 @@ func (n *network) Connect() error { } // dial into NetworkChannel to send network messages - netClient, err := n.Tunnel.Dial(NetworkChannel) + netClient, err := n.Tunnel.Dial(NetworkChannel, tunnel.DialMulticast()) if err != nil { return err } diff --git a/tunnel/default.go b/tunnel/default.go index c7bd52ee..f1a0eaac 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -87,6 +87,12 @@ func (t *tun) getSession(channel, session string) (*session, bool) { return s, ok } +func (t *tun) delSession(channel, session string) { + t.Lock() + delete(t.sessions, channel+session) + t.Unlock() +} + // newSession creates a new session and saves it func (t *tun) newSession(channel, sessionId string) (*session, bool) { // new session @@ -150,7 +156,9 @@ func (t *tun) monitor() { log.Debugf("Tunnel failed to setup node link to %s: %v", node, err) continue } - + // set the link id to the node + // TODO: hash it + link.id = node // save the link t.Lock() t.links[node] = link @@ -169,11 +177,14 @@ func (t *tun) process() { case msg := <-t.send: newMsg := &transport.Message{ Header: make(map[string]string), - Body: msg.data.Body, } - for k, v := range msg.data.Header { - newMsg.Header[k] = v + // set the data + if msg.data != nil { + for k, v := range msg.data.Header { + newMsg.Header[k] = v + } + newMsg.Body = msg.data.Body } // set message head @@ -195,7 +206,7 @@ func (t *tun) process() { t.Lock() if len(t.links) == 0 { - log.Debugf("No links to send to") + log.Debugf("No links to send message type: %s channel: %s", msg.typ, msg.channel) } var sent bool @@ -232,25 +243,55 @@ func (t *tun) process() { continue } + // check the multicast mappings + if msg.multicast { + link.RLock() + _, ok := link.channels[msg.channel] + link.RUnlock() + // channel mapping not found in link + if !ok { + continue + } + } + // send the message via the current link log.Debugf("Sending %+v to %s", newMsg, node) + if errr := link.Send(newMsg); errr != nil { log.Debugf("Tunnel error sending %+v to %s: %v", newMsg, node, errr) err = errors.New(errr.Error()) + // kill the link + link.Close() + // delete the link delete(t.links, node) continue } + // is sent sent = true + + // keep sending broadcast messages + if msg.broadcast || msg.multicast { + continue + } + + // break on unicast + break } t.Unlock() + // set the error if not sent var gerr error if !sent { gerr = err } + // skip if its not been set + if msg.errChan == nil { + continue + } + // return error non blocking select { case msg.errChan <- gerr: @@ -262,14 +303,25 @@ func (t *tun) process() { } } +func (t *tun) delLink(id string) { + t.Lock() + defer t.Unlock() + // get the link + link, ok := t.links[id] + if !ok { + return + } + // close and delete + link.Close() + delete(t.links, id) +} + // process incoming messages func (t *tun) listen(link *link) { // remove the link on exit defer func() { log.Debugf("Tunnel deleting connection from %s", link.Remote()) - t.Lock() - delete(t.links, link.Remote()) - t.Unlock() + t.delLink(link.Remote()) }() // let us know if its a loopback @@ -301,6 +353,13 @@ func (t *tun) listen(link *link) { // the session id sessionId := msg.Header["Micro-Tunnel-Session"] + // if its not connected throw away the link + // the first message we process needs to be connect + if !link.connected && mtype != "connect" { + log.Debugf("Tunnel link %s not connected", link.id) + return + } + switch mtype { case "connect": log.Debugf("Tunnel link %s received connect message", link.Remote()) @@ -311,6 +370,8 @@ func (t *tun) listen(link *link) { loopback = true } + // set to remote node + link.id = id // set as connected link.connected = true @@ -322,10 +383,31 @@ func (t *tun) listen(link *link) { // nothing more to do continue case "close": - log.Debugf("Tunnel link %s closing connection", link.Remote()) // TODO: handle the close message // maybe report io.EOF or kill the link - return + + // close the link entirely + if len(channel) == 0 { + log.Debugf("Tunnel link %s received close message", link.Remote()) + return + } + + // the entire listener was closed so remove it from the mapping + if sessionId == "listener" { + link.Lock() + delete(link.channels, channel) + link.Unlock() + continue + } + + // try get the dialing socket + s, exists := t.getSession(channel, sessionId) + if exists { + // close and continue + s.Close() + continue + } + // otherwise its a session mapping of sorts case "keepalive": log.Debugf("Tunnel link %s received keepalive", link.Remote()) t.Lock() @@ -333,20 +415,64 @@ func (t *tun) listen(link *link) { link.lastKeepAlive = time.Now() t.Unlock() continue + // a new connection dialled outbound + case "open": + // we just let it pass through to be processed + // an accept returned by the listener + case "accept": + + // a continued session case "session": // process message log.Debugf("Received %+v from %s", msg, link.Remote()) + // an announcement of a channel listener + case "announce": + // update mapping in the link + link.Lock() + link.channels[channel] = time.Now() + link.Unlock() + + // get the session that asked for the discovery + s, exists := t.getSession(channel, sessionId) + if exists { + // don't bother it's already discovered + if s.discovered { + continue + } + + // send the announce back to the caller + s.recv <- &message{ + typ: "announce", + tunnel: id, + channel: channel, + session: sessionId, + link: link.id, + } + } + continue + case "discover": + // looking for existing mapping + _, exists := t.getSession(channel, "listener") + if exists { + log.Debugf("Tunnel sending announce for discovery of channel %s", channel) + // send back the announcement + link.Send(&transport.Message{ + Header: map[string]string{ + "Micro-Tunnel": "announce", + "Micro-Tunnel-Id": t.id, + "Micro-Tunnel-Channel": channel, + "Micro-Tunnel-Session": sessionId, + "Micro-Tunnel-Link": link.id, + "Micro-Tunnel-Token": t.token, + }, + }) + } + continue default: // blackhole it continue } - // if its not connected throw away the link - if !link.connected { - log.Debugf("Tunnel link %s not connected", link.id) - return - } - // strip tunnel message header for k, _ := range msg.Header { if strings.HasPrefix(k, "Micro-Tunnel") { @@ -368,8 +494,15 @@ func (t *tun) listen(link *link) { // If its a loopback connection then we've enabled link direction // listening side is used for listening, the dialling side for dialling switch { - case loopback: + case loopback, mtype == "open": s, exists = t.getSession(channel, "listener") + // only return accept to the session + case mtype == "accept": + log.Debugf("Received accept message for %s %s", channel, sessionId) + s, exists = t.getSession(channel, sessionId) + if exists && s.accepted { + continue + } default: // get the session based on the tunnel id and session // this could be something we dialed in which case @@ -383,7 +516,7 @@ func (t *tun) listen(link *link) { } } - // bail if no session has been found + // bail if no session or listener has been found if !exists { log.Debugf("Tunnel skipping no session exists") // drop it, we don't care about @@ -391,8 +524,6 @@ func (t *tun) listen(link *link) { continue } - log.Debugf("Tunnel using session %s %s", s.channel, s.session) - // is the session closed? select { case <-s.closed: @@ -403,6 +534,8 @@ func (t *tun) listen(link *link) { // process } + log.Debugf("Tunnel using channel %s session %s", s.channel, s.session) + // is the session new? select { // if its new the session is actually blocked waiting @@ -462,9 +595,7 @@ func (t *tun) keepalive(link *link) { }, }); err != nil { log.Debugf("Error sending keepalive to link %v: %v", link.Remote(), err) - t.Lock() - delete(t.links, link.Remote()) - t.Unlock() + t.delLink(link.Remote()) return } } @@ -482,6 +613,7 @@ func (t *tun) setupLink(node string) (*link, error) { } log.Debugf("Tunnel connected to %s", node) + // send the first connect message if err := c.Send(&transport.Message{ Header: map[string]string{ "Micro-Tunnel": "connect", @@ -494,9 +626,11 @@ func (t *tun) setupLink(node string) (*link, error) { // create a new link link := newLink(c) - link.connected = true + // set link id to remote side + link.id = c.Remote() // we made the outbound connection // and sent the connect message + link.connected = true // process incoming messages go t.listen(link) @@ -554,7 +688,7 @@ func (t *tun) connect() error { } // save the link - t.links[node] = link + t.links[link.Remote()] = link } // process outbound messages to be sent @@ -628,6 +762,8 @@ func (t *tun) Close() error { return nil } + log.Debug("Tunnel closing") + select { case <-t.closed: return nil @@ -651,7 +787,7 @@ func (t *tun) Close() error { } // Dial an address -func (t *tun) Dial(channel string) (Session, error) { +func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { log.Debugf("Tunnel dialing %s", channel) c, ok := t.newSession(channel, t.newSessionId()) if !ok { @@ -664,18 +800,87 @@ func (t *tun) Dial(channel string) (Session, error) { // outbound session c.outbound = true + // get opts + options := DialOptions{ + Timeout: DefaultDialTimeout, + } + + for _, o := range opts { + o(&options) + } + + // set the multicast option + c.multicast = options.Multicast + // set the dial timeout + c.timeout = options.Timeout + + t.RLock() + for _, link := range t.links { + link.RLock() + _, ok := link.channels[channel] + link.RUnlock() + + // we have at least one channel mapping + if ok { + c.discovered = true + break + } + } + t.RUnlock() + + // shit fuck + if !c.discovered { + t.send <- &message{ + typ: "discover", + tunnel: t.id, + channel: channel, + session: c.session, + broadcast: true, + outbound: true, + errChan: c.errChan, + } + + select { + case err := <-c.errChan: + if err != nil { + return nil, err + } + } + + // wait for announce + select { + case msg := <-c.recv: + if msg.typ != "announce" { + return nil, errors.New("failed to discover channel") + } + } + } + + // try to open the session + err := c.Open() + if err != nil { + // delete the session + t.delSession(c.channel, c.session) + return nil, err + } + return c, nil } // Accept a connection on the address func (t *tun) Listen(channel string) (Listener, error) { log.Debugf("Tunnel listening on %s", channel) + // create a new session by hashing the address c, ok := t.newSession(channel, "listener") if !ok { return nil, errors.New("already listening on " + channel) } + delFunc := func() { + t.delSession(channel, "listener") + } + // set remote. it will be replaced by the first message received c.remote = "remote" // set local @@ -691,6 +896,8 @@ func (t *tun) Listen(channel string) (Listener, error) { tunClosed: t.closed, // the listener session session: c, + // delete session + delFunc: delFunc, } // this kicks off the internal message processor @@ -699,6 +906,9 @@ func (t *tun) Listen(channel string) (Listener, error) { // to the existign sessions go tl.process() + // announces the listener channel to others + go tl.announce() + // return the listener return tl, nil } diff --git a/tunnel/link.go b/tunnel/link.go index fbec2e6a..1e349848 100644 --- a/tunnel/link.go +++ b/tunnel/link.go @@ -9,9 +9,10 @@ import ( ) type link struct { + transport.Socket + sync.RWMutex - transport.Socket // unique id of this link e.g uuid // which we define for ourselves id string @@ -27,11 +28,67 @@ type link struct { // the last time we received a keepalive // on this link from the remote side lastKeepAlive time.Time + // channels keeps a mapping of channels and last seen + channels map[string]time.Time + // stop the link + closed chan bool } func newLink(s transport.Socket) *link { - return &link{ - Socket: s, - id: uuid.New().String(), + l := &link{ + Socket: s, + id: uuid.New().String(), + channels: make(map[string]time.Time), + closed: make(chan bool), + } + go l.run() + return l +} + +func (l *link) run() { + t := time.NewTicker(time.Minute) + defer t.Stop() + + for { + select { + case <-l.closed: + return + case <-t.C: + // drop any channel mappings older than 2 minutes + var kill []string + killTime := time.Minute * 2 + + l.RLock() + for ch, t := range l.channels { + if d := time.Since(t); d > killTime { + kill = append(kill, ch) + } + } + l.RUnlock() + + // if nothing to kill don't both with a wasted lock + if len(kill) == 0 { + continue + } + + // kill the channels! + l.Lock() + for _, ch := range kill { + delete(l.channels, ch) + } + l.Unlock() + } + } +} + +func (l *link) Close() { + l.Lock() + defer l.Unlock() + + select { + case <-l.closed: + return + default: + close(l.closed) } } diff --git a/tunnel/listener.go b/tunnel/listener.go index 4bb8517a..d86e0f80 100644 --- a/tunnel/listener.go +++ b/tunnel/listener.go @@ -2,6 +2,7 @@ package tunnel import ( "io" + "time" "github.com/micro/go-micro/util/log" ) @@ -17,22 +18,77 @@ type tunListener struct { tunClosed chan bool // the listener session session *session + // del func to kill listener + delFunc func() +} + +// periodically announce self +func (t *tunListener) announce() { + tick := time.NewTicker(time.Minute) + defer tick.Stop() + + announce := func() { + msg := &message{ + typ: "announce", + tunnel: t.session.tunnel, + channel: t.session.channel, + session: t.session.session, + outbound: t.session.outbound, + loopback: t.session.loopback, + multicast: t.session.multicast, + } + + select { + case t.session.send <- msg: + case <-t.session.closed: + return + case <-t.closed: + return + } + } + + // first announcement + announce() + + for { + select { + case <-tick.C: + announce() + case <-t.closed: + return + } + } } func (t *tunListener) process() { // our connection map for session conns := make(map[string]*session) + defer func() { + // close the sessions + for _, conn := range conns { + conn.Close() + } + }() + for { select { case <-t.closed: return + case <-t.tunClosed: + t.Close() + return // receive a new message case m := <-t.session.recv: // get a session sess, ok := conns[m.session] log.Debugf("Tunnel listener received channel %s session %s exists: %t", m.channel, m.session, ok) if !ok { + // only create new sessions on open message + if m.typ != "open" { + continue + } + // create a new session session sess = &session{ // the id of the remote side @@ -45,6 +101,8 @@ func (t *tunListener) process() { loopback: m.loopback, // the link the message was received on link: m.link, + // set multicast + multicast: m.multicast, // close chan closed: make(chan bool), // recv called by the acceptor @@ -60,12 +118,39 @@ func (t *tunListener) process() { // save the session conns[m.session] = sess - // send to accept chan select { case <-t.closed: return + // send to accept chan case t.accept <- sess: } + + // continue + continue + } + + // an existing session was found + + // received a close message + switch m.typ { + case "close": + select { + case <-sess.closed: + // no op + delete(conns, m.session) + default: + // close and delete session + close(sess.closed) + delete(conns, m.session) + } + + // continue + continue + case "session": + // operate on this + default: + // non operational type + continue } // send this to the accept chan @@ -89,6 +174,9 @@ func (t *tunListener) Close() error { case <-t.closed: return nil default: + // close and delete + t.delFunc() + t.session.Close() close(t.closed) } return nil @@ -102,13 +190,17 @@ func (t *tunListener) Accept() (Session, error) { return nil, io.EOF case <-t.tunClosed: // close the listener when the tunnel closes - t.Close() return nil, io.EOF // wait for a new connection case c, ok := <-t.accept: + // check if the accept chan is closed if !ok { return nil, io.EOF } + // send back the accept + if err := c.Accept(); err != nil { + return nil, err + } return c, nil } return nil, nil diff --git a/tunnel/options.go b/tunnel/options.go index 9d612173..39795671 100644 --- a/tunnel/options.go +++ b/tunnel/options.go @@ -1,6 +1,8 @@ package tunnel import ( + "time" + "github.com/google/uuid" "github.com/micro/go-micro/transport" "github.com/micro/go-micro/transport/quic" @@ -29,6 +31,15 @@ type Options struct { Transport transport.Transport } +type DialOption func(*DialOptions) + +type DialOptions struct { + // specify a multicast connection + Multicast bool + // the dial timeout + Timeout time.Duration +} + // The tunnel id func Id(id string) Option { return func(o *Options) { @@ -73,3 +84,18 @@ func DefaultOptions() Options { Transport: quic.NewTransport(), } } + +// Dial options + +// Dial multicast sets the multicast option to send only to those mapped +func DialMulticast() DialOption { + return func(o *DialOptions) { + o.Multicast = true + } +} + +func DialTimeout(t time.Duration) DialOption { + return func(o *DialOptions) { + o.Timeout = t + } +} diff --git a/tunnel/session.go b/tunnel/session.go index 41616c7d..464f9bc0 100644 --- a/tunnel/session.go +++ b/tunnel/session.go @@ -3,6 +3,7 @@ package tunnel import ( "errors" "io" + "time" "github.com/micro/go-micro/transport" "github.com/micro/go-micro/util/log" @@ -28,10 +29,20 @@ type session struct { recv chan *message // wait until we have a connection wait chan bool + // if the discovery worked + discovered bool + // if the session was accepted + accepted bool // outbound marks the session as outbound dialled connection outbound bool // lookback marks the session as a loopback on the inbound loopback bool + // if the session is multicast + multicast bool + // if the session is broadcast + broadcast bool + // the timeout + timeout time.Duration // the link on which this message was received link string // the error response @@ -52,6 +63,10 @@ type message struct { outbound bool // loopback marks the message intended for loopback loopback bool + // whether to send as multicast + multicast bool + // broadcast sets the broadcast type + broadcast bool // the link to send the message on link string // transport data @@ -76,10 +91,97 @@ func (s *session) Channel() string { return s.channel } +// Open will fire the open message for the session +func (s *session) Open() error { + msg := &message{ + typ: "open", + tunnel: s.tunnel, + channel: s.channel, + session: s.session, + outbound: s.outbound, + loopback: s.loopback, + multicast: s.multicast, + link: s.link, + errChan: s.errChan, + } + + // send open message + s.send <- msg + + // wait for an error response for send + select { + case err := <-msg.errChan: + if err != nil { + return err + } + case <-s.closed: + return io.EOF + } + + // we don't wait on multicast + if s.multicast { + s.accepted = true + return nil + } + + // now wait for the accept + select { + case msg = <-s.recv: + if msg.typ != "accept" { + log.Debugf("Received non accept message in Open %s", msg.typ) + return errors.New("failed to connect") + } + // set to accepted + s.accepted = true + // set link + s.link = msg.link + case <-time.After(s.timeout): + return ErrDialTimeout + case <-s.closed: + return io.EOF + } + + return nil +} + +func (s *session) Accept() error { + msg := &message{ + typ: "accept", + tunnel: s.tunnel, + channel: s.channel, + session: s.session, + outbound: s.outbound, + loopback: s.loopback, + multicast: s.multicast, + link: s.link, + errChan: s.errChan, + } + + // send the accept message + select { + case <-s.closed: + return io.EOF + case s.send <- msg: + return nil + } + + // wait for send response + select { + case err := <-s.errChan: + if err != nil { + return err + } + case <-s.closed: + return io.EOF + } + + return nil +} + func (s *session) Send(m *transport.Message) error { select { case <-s.closed: - return errors.New("session is closed") + return io.EOF default: // no op } @@ -96,19 +198,26 @@ func (s *session) Send(m *transport.Message) error { // append to backlog msg := &message{ - typ: "session", - tunnel: s.tunnel, - channel: s.channel, - session: s.session, - outbound: s.outbound, - loopback: s.loopback, - data: data, + typ: "session", + tunnel: s.tunnel, + channel: s.channel, + session: s.session, + outbound: s.outbound, + loopback: s.loopback, + multicast: s.multicast, + data: data, // specify the link on which to send this // it will be blank for dialled sessions link: s.link, // error chan errChan: s.errChan, } + + // if not multicast then set link + if !s.multicast { + msg.link = s.link + } + log.Debugf("Appending %+v to send backlog", msg) s.send <- msg @@ -154,6 +263,25 @@ func (s *session) Close() error { // no op default: close(s.closed) + + // append to backlog + msg := &message{ + typ: "close", + tunnel: s.tunnel, + channel: s.channel, + session: s.session, + outbound: s.outbound, + loopback: s.loopback, + multicast: s.multicast, + link: s.link, + } + + // send the close message + select { + case s.send <- msg: + default: + } } + return nil } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 58c0ac27..a4ef7da2 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -2,6 +2,9 @@ package tunnel import ( + "errors" + "time" + "github.com/micro/go-micro/transport" ) @@ -18,7 +21,7 @@ type Tunnel interface { // Close closes the tunnel Close() error // Connect to a channel - Dial(channel string) (Session, error) + Dial(channel string, opts ...DialOption) (Session, error) // Accept connections on a channel Listen(channel string) (Listener, error) // Name of the tunnel implementation @@ -42,6 +45,12 @@ type Session interface { transport.Socket } +var ( + ErrDialTimeout = errors.New("dial timeout") + + DefaultDialTimeout = time.Second * 5 +) + // NewTunnel creates a new tunnel func NewTunnel(opts ...Option) Tunnel { return newTunnel(opts...) From 66db0ac52c9c17e6be2f10ba5bb5167cf9ba799a Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 4 Sep 2019 11:58:03 +0100 Subject: [PATCH 55/93] Move announce into session --- tunnel/listener.go | 24 ++---------------------- tunnel/session.go | 28 ++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/tunnel/listener.go b/tunnel/listener.go index d86e0f80..03ada54a 100644 --- a/tunnel/listener.go +++ b/tunnel/listener.go @@ -27,33 +27,13 @@ func (t *tunListener) announce() { tick := time.NewTicker(time.Minute) defer tick.Stop() - announce := func() { - msg := &message{ - typ: "announce", - tunnel: t.session.tunnel, - channel: t.session.channel, - session: t.session.session, - outbound: t.session.outbound, - loopback: t.session.loopback, - multicast: t.session.multicast, - } - - select { - case t.session.send <- msg: - case <-t.session.closed: - return - case <-t.closed: - return - } - } - // first announcement - announce() + t.session.Announce() for { select { case <-tick.C: - announce() + t.session.Announce() case <-t.closed: return } diff --git a/tunnel/session.go b/tunnel/session.go index 464f9bc0..9e431296 100644 --- a/tunnel/session.go +++ b/tunnel/session.go @@ -91,7 +91,7 @@ func (s *session) Channel() string { return s.channel } -// Open will fire the open message for the session +// Open will fire the open message for the session. This is called by the dialler. func (s *session) Open() error { msg := &message{ typ: "open", @@ -144,6 +144,7 @@ func (s *session) Open() error { return nil } +// Accept sends the accept response to an open message from a dialled connection func (s *session) Accept() error { msg := &message{ typ: "accept", @@ -178,6 +179,27 @@ func (s *session) Accept() error { return nil } +// Announce sends an announcement to notify that this session exists. This is primarily used by the listener. +func (s *session) Announce() error { + msg := &message{ + typ: "announce", + tunnel: s.tunnel, + channel: s.channel, + session: s.session, + outbound: s.outbound, + loopback: s.loopback, + multicast: s.multicast, + } + + select { + case s.send <- msg: + return nil + case <-s.closed: + return io.EOF + } +} + +// Send is used to send a message func (s *session) Send(m *transport.Message) error { select { case <-s.closed: @@ -219,6 +241,7 @@ func (s *session) Send(m *transport.Message) error { } log.Debugf("Appending %+v to send backlog", msg) + // send the actual message s.send <- msg // wait for an error response @@ -232,6 +255,7 @@ func (s *session) Send(m *transport.Message) error { return nil } +// Recv is used to receive a message func (s *session) Recv(m *transport.Message) error { select { case <-s.closed: @@ -256,7 +280,7 @@ func (s *session) Recv(m *transport.Message) error { return nil } -// Close closes the session +// Close closes the session by sending a close message func (s *session) Close() error { select { case <-s.closed: From a24818ee54dbe84497874ad765c9c98f73635d4c Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 4 Sep 2019 11:58:25 +0100 Subject: [PATCH 56/93] Fix typo --- tunnel/link.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tunnel/link.go b/tunnel/link.go index 1e349848..7e14e9d8 100644 --- a/tunnel/link.go +++ b/tunnel/link.go @@ -66,7 +66,7 @@ func (l *link) run() { } l.RUnlock() - // if nothing to kill don't both with a wasted lock + // if nothing to kill don't bother with a wasted lock if len(kill) == 0 { continue } From c718b8bf93fbb904c266879a7ddc8b401b8a2981 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 4 Sep 2019 12:00:11 +0100 Subject: [PATCH 57/93] Move vars and comment --- tunnel/tunnel.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index a4ef7da2..9566f3b9 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -8,6 +8,14 @@ import ( "github.com/micro/go-micro/transport" ) +var ( + // ErrDialTimeout is returned by a call to Dial where the timeout occurs + ErrDialTimeout = errors.New("dial timeout") + // DefaultDialTimeout is the dial timeout if none is specified + DefaultDialTimeout = time.Second * 5 +) + + // Tunnel creates a gre tunnel on top of the go-micro/transport. // It establishes multiple streams using the Micro-Tunnel-Channel header // and Micro-Tunnel-Session header. The tunnel id is a hash of @@ -45,12 +53,6 @@ type Session interface { transport.Socket } -var ( - ErrDialTimeout = errors.New("dial timeout") - - DefaultDialTimeout = time.Second * 5 -) - // NewTunnel creates a new tunnel func NewTunnel(opts ...Option) Tunnel { return newTunnel(opts...) From d5be2136ade68d94b9d01859bd887e947e8d5ee0 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 4 Sep 2019 12:16:31 +0100 Subject: [PATCH 58/93] cleanup new message creation --- tunnel/default.go | 16 +++++----- tunnel/session.go | 78 +++++++++++++++-------------------------------- tunnel/tunnel.go | 1 - 3 files changed, 32 insertions(+), 63 deletions(-) diff --git a/tunnel/default.go b/tunnel/default.go index f1a0eaac..0ab11ea2 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -830,15 +830,13 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { // shit fuck if !c.discovered { - t.send <- &message{ - typ: "discover", - tunnel: t.id, - channel: channel, - session: c.session, - broadcast: true, - outbound: true, - errChan: c.errChan, - } + msg := c.newMessage("discover") + msg.broadcast = true + msg.outbound = true + msg.link = "" + + // send the discovery message + t.send <- msg select { case err := <-c.errChan: diff --git a/tunnel/session.go b/tunnel/session.go index 9e431296..eb3cc170 100644 --- a/tunnel/session.go +++ b/tunnel/session.go @@ -91,10 +91,9 @@ func (s *session) Channel() string { return s.channel } -// Open will fire the open message for the session. This is called by the dialler. -func (s *session) Open() error { - msg := &message{ - typ: "open", +func (s *session) newMessage(typ string) *message { + return &message{ + typ: typ, tunnel: s.tunnel, channel: s.channel, session: s.session, @@ -104,6 +103,12 @@ func (s *session) Open() error { link: s.link, errChan: s.errChan, } +} + +// Open will fire the open message for the session. This is called by the dialler. +func (s *session) Open() error { + // create a new message + msg := s.newMessage("open") // send open message s.send <- msg @@ -146,17 +151,7 @@ func (s *session) Open() error { // Accept sends the accept response to an open message from a dialled connection func (s *session) Accept() error { - msg := &message{ - typ: "accept", - tunnel: s.tunnel, - channel: s.channel, - session: s.session, - outbound: s.outbound, - loopback: s.loopback, - multicast: s.multicast, - link: s.link, - errChan: s.errChan, - } + msg := s.newMessage("accept") // send the accept message select { @@ -181,15 +176,11 @@ func (s *session) Accept() error { // Announce sends an announcement to notify that this session exists. This is primarily used by the listener. func (s *session) Announce() error { - msg := &message{ - typ: "announce", - tunnel: s.tunnel, - channel: s.channel, - session: s.session, - outbound: s.outbound, - loopback: s.loopback, - multicast: s.multicast, - } + msg := s.newMessage("announce") + // we don't need an error back + msg.errChan = nil + // we don't need the link + msg.link = "" select { case s.send <- msg: @@ -218,26 +209,14 @@ func (s *session) Send(m *transport.Message) error { data.Header[k] = v } - // append to backlog - msg := &message{ - typ: "session", - tunnel: s.tunnel, - channel: s.channel, - session: s.session, - outbound: s.outbound, - loopback: s.loopback, - multicast: s.multicast, - data: data, - // specify the link on which to send this - // it will be blank for dialled sessions - link: s.link, - // error chan - errChan: s.errChan, - } + // create a new message + msg := s.newMessage("session") + // set the data + msg.data = data - // if not multicast then set link - if !s.multicast { - msg.link = s.link + // if multicast don't set the link + if s.multicast { + msg.link = "" } log.Debugf("Appending %+v to send backlog", msg) @@ -289,16 +268,9 @@ func (s *session) Close() error { close(s.closed) // append to backlog - msg := &message{ - typ: "close", - tunnel: s.tunnel, - channel: s.channel, - session: s.session, - outbound: s.outbound, - loopback: s.loopback, - multicast: s.multicast, - link: s.link, - } + msg := s.newMessage("close") + // no error response on close + msg.errChan = nil // send the close message select { diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 9566f3b9..e76f8437 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -15,7 +15,6 @@ var ( DefaultDialTimeout = time.Second * 5 ) - // Tunnel creates a gre tunnel on top of the go-micro/transport. // It establishes multiple streams using the Micro-Tunnel-Channel header // and Micro-Tunnel-Session header. The tunnel id is a hash of From 0075477df0679734a64c10b31069f7d65bec8419 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 4 Sep 2019 12:18:31 +0100 Subject: [PATCH 59/93] make tunnel broker use multicast --- tunnel/broker/broker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tunnel/broker/broker.go b/tunnel/broker/broker.go index 0a33c610..6778dfaa 100644 --- a/tunnel/broker/broker.go +++ b/tunnel/broker/broker.go @@ -58,7 +58,7 @@ func (t *tunBroker) Disconnect() error { func (t *tunBroker) Publish(topic string, m *broker.Message, opts ...broker.PublishOption) error { // TODO: this is probably inefficient, we might want to just maintain an open connection // it may be easier to add broadcast to the tunnel - c, err := t.tunnel.Dial(topic) + c, err := t.tunnel.Dial(topic, tunnel.DialMulticast()) if err != nil { return err } From 7ab3934eb7e68907d3482c28e666b9643e39be1d Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 4 Sep 2019 12:18:37 +0100 Subject: [PATCH 60/93] add message comment --- tunnel/session.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tunnel/session.go b/tunnel/session.go index eb3cc170..58246fdd 100644 --- a/tunnel/session.go +++ b/tunnel/session.go @@ -91,6 +91,7 @@ func (s *session) Channel() string { return s.channel } +// newMessage creates a new message based on the session func (s *session) newMessage(typ string) *message { return &message{ typ: typ, From d559ce9da2efaa493960fada7bf4a310a8b72eb8 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 4 Sep 2019 15:41:57 +0100 Subject: [PATCH 61/93] Provide Links() method in Tunnel --- tunnel/default.go | 13 +++++++++++++ tunnel/link.go | 14 ++++++++++++-- tunnel/tunnel.go | 10 ++++++++++ tunnel/tunnel_test.go | 23 +++++------------------ 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/tunnel/default.go b/tunnel/default.go index 0ab11ea2..d62dd66e 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -911,6 +911,19 @@ func (t *tun) Listen(channel string) (Listener, error) { return tl, nil } +func (t *tun) Links() []Link { + t.RLock() + defer t.RUnlock() + + var links []Link + + for _, link := range t.links { + links = append(links, link) + } + + return links +} + func (t *tun) String() string { return "mucp" } diff --git a/tunnel/link.go b/tunnel/link.go index 7e14e9d8..9470ef6a 100644 --- a/tunnel/link.go +++ b/tunnel/link.go @@ -81,14 +81,24 @@ func (l *link) run() { } } -func (l *link) Close() { +func (l *link) Id() string { + l.RLock() + defer l.RUnlock() + + return l.id +} + +func (l *link) Close() error { l.Lock() defer l.Unlock() select { case <-l.closed: - return + return nil default: close(l.closed) + return l.Socket.Close() } + + return nil } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index e76f8437..7c9e2afc 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -31,10 +31,20 @@ type Tunnel interface { Dial(channel string, opts ...DialOption) (Session, error) // Accept connections on a channel Listen(channel string) (Listener, error) + // All the links the tunnel is connected to + Links() []Link // Name of the tunnel implementation String() string } +// Link represents internal links to the tunnel +type Link interface { + // The id of the link + Id() string + // honours transport socket + transport.Socket +} + // The listener provides similar constructs to the transport.Listener type Listener interface { Accept() (Session, error) diff --git a/tunnel/tunnel_test.go b/tunnel/tunnel_test.go index 3fc84b6d..fc76421b 100644 --- a/tunnel/tunnel_test.go +++ b/tunnel/tunnel_test.go @@ -187,30 +187,15 @@ func testBrokenTunAccept(t *testing.T, tun Tunnel, wait chan bool, wg *sync.Wait if err := c.Recv(m); err != nil { t.Fatal(err) } - tun.Close() - // re-start tunnel - err = tun.Connect() - if err != nil { - t.Fatal(err) - } - defer tun.Close() - - // listen on some virtual address - tl, err = tun.Listen("test-tunnel") - if err != nil { - t.Fatal(err) + // close all the links + for _, link := range tun.Links() { + link.Close() } // receiver ready; notify sender wait <- true - // accept a connection - c, err = tl.Accept() - if err != nil { - t.Fatal(err) - } - // accept the message m = new(transport.Message) if err := c.Recv(m); err != nil { @@ -279,6 +264,7 @@ func TestReconnectTunnel(t *testing.T) { if err != nil { t.Fatal(err) } + defer tunB.Close() // we manually override the tunnel.ReconnectTime value here // this is so that we make the reconnects faster than the default 5s @@ -289,6 +275,7 @@ func TestReconnectTunnel(t *testing.T) { if err != nil { t.Fatal(err) } + defer tunA.Close() wait := make(chan bool) From 407381912bac6f7f16f61d38cfd410f8c209bf03 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 4 Sep 2019 15:55:37 +0100 Subject: [PATCH 62/93] Don't try discover on multicast, don't block existing sessions on listen --- tunnel/default.go | 8 ++++++++ tunnel/listener.go | 8 +++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/tunnel/default.go b/tunnel/default.go index d62dd66e..5d27809a 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -814,6 +814,14 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { // set the dial timeout c.timeout = options.Timeout + // don't bother with the song and dance below + // we're just going to assume things come online + // as and when. + if c.multicast { + return c, nil + } + + // non multicast so we need to find the link t.RLock() for _, link := range t.links { link.RLock() diff --git a/tunnel/listener.go b/tunnel/listener.go index 03ada54a..e60ff396 100644 --- a/tunnel/listener.go +++ b/tunnel/listener.go @@ -64,8 +64,9 @@ func (t *tunListener) process() { sess, ok := conns[m.session] log.Debugf("Tunnel listener received channel %s session %s exists: %t", m.channel, m.session, ok) if !ok { - // only create new sessions on open message - if m.typ != "open" { + switch m.typ { + case "open", "session": + default: continue } @@ -104,9 +105,6 @@ func (t *tunListener) process() { // send to accept chan case t.accept <- sess: } - - // continue - continue } // an existing session was found From dd9f42e3b95f244e08f418d837db9c346d3a7b31 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Wed, 4 Sep 2019 18:02:13 +0100 Subject: [PATCH 63/93] Update lastSeen timestamp properly. Set lastSeen when processing advert --- network/default.go | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/network/default.go b/network/default.go index 202e18f1..13b106b2 100644 --- a/network/default.go +++ b/network/default.go @@ -288,6 +288,8 @@ func (n *network) processNetChan(l tunnel.Listener) { // switch on type of message and take action switch m.Header["Micro-Method"] { case "connect": + // mark the time the message has been received + now := time.Now() pbNetConnect := &pbNet.Connect{} if err := proto.Unmarshal(m.Body, pbNetConnect); err != nil { log.Debugf("Network tunnel [%s] connect unmarshal error: %v", NetworkChannel, err) @@ -298,20 +300,28 @@ func (n *network) processNetChan(l tunnel.Listener) { continue } n.Lock() + log.Debugf("Network connect message received from: %s", pbNetConnect.Node.Id) // if the entry already exists skip adding it - if _, ok := n.neighbours[pbNetConnect.Node.Id]; ok { + if neighbour, ok := n.neighbours[pbNetConnect.Node.Id]; ok { + // update lastSeen timestamp + if n.neighbours[pbNetConnect.Node.Id].lastSeen.Before(now) { + neighbour.lastSeen = now + } n.Unlock() continue } - // add a new neighbour; + // add a new neighbour // NOTE: new node does not have any neighbours n.neighbours[pbNetConnect.Node.Id] = &node{ id: pbNetConnect.Node.Id, address: pbNetConnect.Node.Address, neighbours: make(map[string]*node), + lastSeen: now, } n.Unlock() case "neighbour": + // mark the time the message has been received + now := time.Now() pbNetNeighbour := &pbNet.Neighbour{} if err := proto.Unmarshal(m.Body, pbNetNeighbour); err != nil { log.Debugf("Network tunnel [%s] neighbour unmarshal error: %v", NetworkChannel, err) @@ -322,15 +332,19 @@ func (n *network) processNetChan(l tunnel.Listener) { continue } n.Lock() + log.Debugf("Network neighbour message received from: %s", pbNetNeighbour.Node.Id) // only add the neighbour if it's not already in the neighbourhood if _, ok := n.neighbours[pbNetNeighbour.Node.Id]; !ok { - neighbour := &node{ + n.neighbours[pbNetNeighbour.Node.Id] = &node{ id: pbNetNeighbour.Node.Id, address: pbNetNeighbour.Node.Address, neighbours: make(map[string]*node), - lastSeen: time.Now(), + lastSeen: now, } - n.neighbours[pbNetNeighbour.Node.Id] = neighbour + } + // update lastSeen timestamp + if n.neighbours[pbNetNeighbour.Node.Id].lastSeen.Before(now) { + n.neighbours[pbNetNeighbour.Node.Id].lastSeen = now } // update/store the neighbour node neighbours for _, pbNeighbour := range pbNetNeighbour.Neighbours { @@ -430,6 +444,7 @@ func (n *network) pruneNode(id string) error { return err } // delete the found routes + log.Logf("Network deleting routes originated by router: %s", id) for _, route := range routes { if err := n.Router.Table().Delete(route); err != nil && err != router.ErrRouteNotFound { return err @@ -452,8 +467,10 @@ func (n *network) prune() { case <-prune.C: n.Lock() for id, node := range n.neighbours { - nodeAge := time.Since(node.lastSeen) - if nodeAge > PruneTime { + if id == n.options.Id { + continue + } + if time.Since(node.lastSeen) > PruneTime { log.Debugf("Network deleting node %s: reached prune time threshold", id) if err := n.pruneNode(id); err != nil { log.Debugf("Network failed to prune the node %s: %v", id, err) @@ -555,14 +572,19 @@ func (n *network) processCtrlChan(l tunnel.Listener) { // switch on type of message and take action switch m.Header["Micro-Method"] { case "advert": + now := time.Now() pbRtrAdvert := &pbRtr.Advert{} if err := proto.Unmarshal(m.Body, pbRtrAdvert); err != nil { log.Debugf("Network fail to unmarshal advert message: %v", err) continue } - + // don't process your own messages + if pbRtrAdvert.Id == n.options.Id { + continue + } // loookup advertising node in our neighbourhood n.RLock() + log.Debugf("Network received advert message from: %s", pbRtrAdvert.Id) advertNode, ok := n.neighbours[pbRtrAdvert.Id] if !ok { // advertising node has not been registered as our neighbour, yet @@ -570,6 +592,7 @@ func (n *network) processCtrlChan(l tunnel.Listener) { advertNode = &node{ id: pbRtrAdvert.Id, neighbours: make(map[string]*node), + lastSeen: now, } n.neighbours[pbRtrAdvert.Id] = advertNode } @@ -785,6 +808,7 @@ func (n *network) Connect() error { Body: body, } + log.Debugf("Network sending connect message: %s", node.Id) if err := netClient.Send(&m); err != nil { log.Debugf("Network failed to send connect messsage: %v", err) } From b9a2f719a02eb1fcb64e55a84201cbc477e47974 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 4 Sep 2019 18:46:20 +0100 Subject: [PATCH 64/93] Add some fixes --- tunnel/default.go | 68 +++++++++++++++++++++++++++++++++-------------- tunnel/link.go | 2 +- 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/tunnel/default.go b/tunnel/default.go index 5d27809a..add48eb8 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -220,14 +220,6 @@ func (t *tun) process() { continue } - // if we're picking the link check the id - // this is where we explicitly set the link - // in a message received via the listen method - if len(msg.link) > 0 && link.id != msg.link { - err = errors.New("link not found") - continue - } - // if the link was a loopback accepted connection // and the message is being sent outbound via // a dialled connection don't use this link @@ -252,6 +244,14 @@ func (t *tun) process() { if !ok { continue } + } else { + // if we're picking the link check the id + // this is where we explicitly set the link + // in a message received via the listen method + if len(msg.link) > 0 && link.id != msg.link { + err = errors.New("link not found") + continue + } } // send the message via the current link @@ -364,6 +364,7 @@ func (t *tun) listen(link *link) { case "connect": log.Debugf("Tunnel link %s received connect message", link.Remote()) + link.Lock() // are we connecting to ourselves? if id == t.id { link.loopback = true @@ -374,6 +375,7 @@ func (t *tun) listen(link *link) { link.id = id // set as connected link.connected = true + link.Unlock() // save the link once connected t.Lock() @@ -417,10 +419,19 @@ func (t *tun) listen(link *link) { continue // a new connection dialled outbound case "open": + log.Debugf("Tunnel link %s received open %s %s", link.id, channel, sessionId) // we just let it pass through to be processed // an accept returned by the listener case "accept": - + s, exists := t.getSession(channel, sessionId) + // we don't need this + if exists && s.multicast { + s.accepted = true + continue + } + if exists && s.accepted { + continue + } // a continued session case "session": // process message @@ -725,6 +736,12 @@ func (t *tun) Connect() error { } func (t *tun) close() error { + // close all the sessions + for id, s := range t.sessions { + s.Close() + delete(t.sessions, id) + } + // close all the links for node, link := range t.links { link.Send(&transport.Message{ @@ -768,12 +785,6 @@ func (t *tun) Close() error { case <-t.closed: return nil default: - // close all the sessions - for id, s := range t.sessions { - s.Close() - delete(t.sessions, id) - } - // close the connection close(t.closed) t.connected = false @@ -814,11 +825,16 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { // set the dial timeout c.timeout = options.Timeout - // don't bother with the song and dance below - // we're just going to assume things come online - // as and when. - if c.multicast { - return c, nil + now := time.Now() + + after := func() time.Duration { + d := time.Since(now) + // dial timeout minus time since + wait := options.Timeout - d + if wait < time.Duration(0) { + return time.Duration(0) + } + return wait } // non multicast so we need to find the link @@ -846,7 +862,17 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { // send the discovery message t.send <- msg + // don't bother waiting around + // we're just going to assume things come online + if c.multicast { + c.discovered = true + c.accepted = true + return c, nil + } + select { + case <-time.After(after()): + return nil, ErrDialTimeout case err := <-c.errChan: if err != nil { return nil, err @@ -859,6 +885,8 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { if msg.typ != "announce" { return nil, errors.New("failed to discover channel") } + case <-time.After(after()): + return nil, ErrDialTimeout } } diff --git a/tunnel/link.go b/tunnel/link.go index 9470ef6a..38c80825 100644 --- a/tunnel/link.go +++ b/tunnel/link.go @@ -97,7 +97,7 @@ func (l *link) Close() error { return nil default: close(l.closed) - return l.Socket.Close() + return nil } return nil From d8a1b479544893035d8dbc1e1caf6d8d5d27967c Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 4 Sep 2019 18:48:43 +0100 Subject: [PATCH 65/93] Remove lock from link --- tunnel/link.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/tunnel/link.go b/tunnel/link.go index 38c80825..b360e826 100644 --- a/tunnel/link.go +++ b/tunnel/link.go @@ -89,9 +89,6 @@ func (l *link) Id() string { } func (l *link) Close() error { - l.Lock() - defer l.Unlock() - select { case <-l.closed: return nil From e15389febb1a55ae5edb0678765e69e022515ff4 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 4 Sep 2019 20:18:26 +0100 Subject: [PATCH 66/93] Fix massive cruft in tunnel dial to set the link on discovered --- tunnel/default.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tunnel/default.go b/tunnel/default.go index add48eb8..860b6a96 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -2,6 +2,7 @@ package tunnel import ( "errors" + "math/rand" "strings" "sync" "time" @@ -529,7 +530,7 @@ func (t *tun) listen(link *link) { // bail if no session or listener has been found if !exists { - log.Debugf("Tunnel skipping no session exists") + log.Debugf("Tunnel skipping no session %s %s exists", channel, sessionId) // drop it, we don't care about // messages we don't know about continue @@ -837,6 +838,8 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { return wait } + var links []string + // non multicast so we need to find the link t.RLock() for _, link := range t.links { @@ -847,11 +850,18 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { // we have at least one channel mapping if ok { c.discovered = true - break + links = append(links, link.id) } } t.RUnlock() + // discovered so set the link + if c.discovered { + // set the link + i := rand.Intn(len(links)) + c.link = links[i] + } + // shit fuck if !c.discovered { msg := c.newMessage("discover") From cc5d811a83fdd1c805727fb256de2013822e2b23 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 4 Sep 2019 20:19:53 +0100 Subject: [PATCH 67/93] add comment to tunnel link selection --- tunnel/default.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tunnel/default.go b/tunnel/default.go index 860b6a96..2d012dff 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -856,6 +856,8 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { t.RUnlock() // discovered so set the link + // TODO: pick the link efficiently based + // on link status and saturation. if c.discovered { // set the link i := rand.Intn(len(links)) From 4b1a7abb42a1cc85e4862906b7b694b89e6cc12d Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Thu, 5 Sep 2019 00:16:22 +0100 Subject: [PATCH 68/93] Freeze network graph when building full network topology Also added some comments and debug logs --- network/default.go | 77 ++++++++++++++++++++++---------------- network/handler/handler.go | 1 + 2 files changed, 46 insertions(+), 32 deletions(-) diff --git a/network/default.go b/network/default.go index 4a6d4bb4..94ee1d25 100644 --- a/network/default.go +++ b/network/default.go @@ -300,7 +300,7 @@ func (n *network) processNetChan(l tunnel.Listener) { continue } n.Lock() - log.Debugf("Network connect message received from: %s", pbNetConnect.Node.Id) + log.Debugf("Network received connect message from: %s", pbNetConnect.Node.Id) // if the entry already exists skip adding it if neighbour, ok := n.neighbours[pbNetConnect.Node.Id]; ok { // update lastSeen timestamp @@ -332,8 +332,8 @@ func (n *network) processNetChan(l tunnel.Listener) { continue } n.Lock() - log.Debugf("Network neighbour message received from: %s", pbNetNeighbour.Node.Id) - // only add the neighbour if it's not already in the neighbourhood + log.Debugf("Network received neighbour message from: %s", pbNetNeighbour.Node.Id) + // only add the neighbour if it is NOT already in node's list of neighbours if _, ok := n.neighbours[pbNetNeighbour.Node.Id]; !ok { n.neighbours[pbNetNeighbour.Node.Id] = &node{ id: pbNetNeighbour.Node.Id, @@ -347,10 +347,14 @@ func (n *network) processNetChan(l tunnel.Listener) { n.neighbours[pbNetNeighbour.Node.Id].lastSeen = now } // update/store the neighbour node neighbours + // NOTE: * we dont update lastSeen time for the neighbours of the neighbour + // * even though we are NOT interested in neighbours of neighbours here + // we still allocate the map of neighbours for each of them for _, pbNeighbour := range pbNetNeighbour.Neighbours { neighbourNode := &node{ - id: pbNeighbour.Id, - address: pbNeighbour.Address, + id: pbNeighbour.Id, + address: pbNeighbour.Address, + neighbours: make(map[string]*node), } n.neighbours[pbNetNeighbour.Node.Id].neighbours[neighbourNode.id] = neighbourNode } @@ -366,6 +370,7 @@ func (n *network) processNetChan(l tunnel.Listener) { continue } n.Lock() + log.Debugf("Network received close message from: %s", pbNetClose.Node.Id) if err := n.pruneNode(pbNetClose.Node.Id); err != nil { log.Debugf("Network failed to prune the node %s: %v", pbNetClose.Node.Id, err) continue @@ -423,6 +428,7 @@ func (n *network) announce(client transport.Client) { Body: body, } + log.Debugf("Network sending neighbour message from: %s", node.Id) if err := client.Send(&m); err != nil { log.Debugf("Network failed to send neighbour messsage: %v", err) continue @@ -704,6 +710,7 @@ func (n *network) advertise(client transport.Client, advertChan <-chan *router.A Body: body, } + log.Debugf("Network sending advert message from: %s", pbRtrAdvert.Id) if err := client.Send(&m); err != nil { log.Debugf("Network failed to send advert %s: %v", pbRtrAdvert.Id, err) continue @@ -839,6 +846,12 @@ func (n *network) Nodes() []Node { visited := make(map[string]*node) // queue of the nodes to visit queue := list.New() + + // we need to freeze the network graph here + // otherwise we might get invalid results + n.RLock() + defer n.RUnlock() + // push network node to the back of queue queue.PushBack(n.node) // mark the node as visited @@ -898,39 +911,39 @@ func (n *network) Close() error { case <-n.closed: return nil default: + // send close message only if we managed to connect to NetworkChannel + if netClient, ok := n.tunClient[NetworkChannel]; ok { + // send connect message to NetworkChannel + node := &pbNet.Node{ + Id: n.options.Id, + Address: n.options.Address, + } + pbNetClose := &pbNet.Close{ + Node: node, + } + + // only proceed with sending to NetworkChannel if marshal succeeds + if body, err := proto.Marshal(pbNetClose); err == nil { + // create transport message and chuck it down the pipe + m := transport.Message{ + Header: map[string]string{ + "Micro-Method": "close", + }, + Body: body, + } + + log.Debugf("Network sending close message from: %s", node.Id) + if err := netClient.Send(&m); err != nil { + log.Debugf("Network failed to send close messsage: %v", err) + } + } + } // TODO: send close message to the network channel close(n.closed) // set connected to false n.connected = false } - // send close message only if we managed to connect to NetworkChannel - if netClient, ok := n.tunClient[NetworkChannel]; ok { - // send connect message to NetworkChannel - node := &pbNet.Node{ - Id: n.options.Id, - Address: n.options.Address, - } - pbNetClose := &pbNet.Close{ - Node: node, - } - - // only proceed with sending to NetworkChannel if marshal succeeds - if body, err := proto.Marshal(pbNetClose); err == nil { - // create transport message and chuck it down the pipe - m := transport.Message{ - Header: map[string]string{ - "Micro-Method": "close", - }, - Body: body, - } - - if err := netClient.Send(&m); err != nil { - log.Debugf("Network failed to send close messsage: %v", err) - } - } - } - return n.close() } diff --git a/network/handler/handler.go b/network/handler/handler.go index 3740d4a9..1d8d74a8 100644 --- a/network/handler/handler.go +++ b/network/handler/handler.go @@ -1,3 +1,4 @@ +// Package handler implements network RPC handler package handler import ( From a1ba1482c5a58b3049cc35392137fa590510fd0b Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Thu, 5 Sep 2019 07:41:19 +0100 Subject: [PATCH 69/93] Only set link if not multicast --- tunnel/default.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tunnel/default.go b/tunnel/default.go index 2d012dff..1aa5d2bf 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -855,10 +855,10 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { } t.RUnlock() - // discovered so set the link + // discovered so set the link if not multicast // TODO: pick the link efficiently based // on link status and saturation. - if c.discovered { + if c.discovered && !c.multicast { // set the link i := rand.Intn(len(links)) c.link = links[i] From 9161b20d6b211ddbbfeead8272fc556a5f66c76e Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Thu, 5 Sep 2019 13:23:33 +0100 Subject: [PATCH 70/93] Add Solicit method to router interface When calling Solicit, router lists all the routes and advertise them straight away --- router/default.go | 54 +++++++++++++++++++++++++++++++++++------------ router/router.go | 2 ++ 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/router/default.go b/router/default.go index 05e033ce..1fa8161a 100644 --- a/router/default.go +++ b/router/default.go @@ -602,21 +602,10 @@ func (r *router) Advertise() (<-chan *Advert, error) { r.subscribers[uuid.New().String()] = advertChan return advertChan, nil case Running: - // list routing table routes to announce - routes, err := r.table.List() + // list all the routes and pack them into even slice to advertise + events, err := r.flushRouteEvents() if err != nil { - return nil, fmt.Errorf("failed listing routes: %s", err) - } - - // collect all the added routes before we attempt to add default gateway - events := make([]*Event, len(routes)) - for i, route := range routes { - event := &Event{ - Type: Create, - Timestamp: time.Now(), - Route: route, - } - events[i] = event + return nil, fmt.Errorf("failed to advertise routes: %s", err) } // create event channels @@ -687,6 +676,43 @@ func (r *router) Process(a *Advert) error { return nil } +// flushRouteEvents lists all the routes and builds a slice of events +func (r *router) flushRouteEvents() ([]*Event, error) { + // list all routes + routes, err := r.table.List() + if err != nil { + return nil, fmt.Errorf("failed listing routes: %s", err) + } + + // build a list of events to advertise + events := make([]*Event, len(routes)) + for i, route := range routes { + event := &Event{ + Type: Create, + Timestamp: time.Now(), + Route: route, + } + events[i] = event + } + + return events, nil +} + +// Solicit advertises all of its routes to the network +// It returns error if the router fails to list the routes +func (r *router) Solicit() error { + events, err := r.flushRouteEvents() + if err != nil { + return fmt.Errorf("failed solicit routes: %s", err) + } + + // advertise the routes + r.advertWg.Add(1) + go r.publishAdvert(RouteUpdate, events) + + return nil +} + // Lookup routes in the routing table func (r *router) Lookup(q Query) ([]Route, error) { return r.table.Query(q) diff --git a/router/router.go b/router/router.go index 8b6618d1..224ef124 100644 --- a/router/router.go +++ b/router/router.go @@ -28,6 +28,8 @@ type Router interface { Advertise() (<-chan *Advert, error) // Process processes incoming adverts Process(*Advert) error + // Solicit advertises the whole routing table ot the network + Solicit() error // Lookup queries routes in the routing table Lookup(Query) ([]Route, error) // Watch returns a watcher which tracks updates to the routing table From 1840b5bd74f2c26a3eb63bbc756befce71875086 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Thu, 5 Sep 2019 15:16:11 +0100 Subject: [PATCH 71/93] Update tunnel to send discovery on connect and multicast messages. Announce as broadcast --- tunnel/default.go | 130 +++++++++++++++++++++++++++++++++++---------- tunnel/listener.go | 2 +- tunnel/session.go | 2 + 3 files changed, 105 insertions(+), 29 deletions(-) diff --git a/tunnel/default.go b/tunnel/default.go index 1aa5d2bf..6b74bfa8 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -94,6 +94,21 @@ func (t *tun) delSession(channel, session string) { t.Unlock() } +// listChannels returns a list of listening channels +func (t *tun) listChannels() []string { + t.RLock() + defer t.RUnlock() + + var channels []string + for _, session := range t.sessions { + if session.session != "listener" { + continue + } + channels = append(channels, session.channel) + } + return channels +} + // newSession creates a new session and saves it func (t *tun) newSession(channel, sessionId string) (*session, bool) { // new session @@ -439,11 +454,20 @@ func (t *tun) listen(link *link) { log.Debugf("Received %+v from %s", msg, link.Remote()) // an announcement of a channel listener case "announce": + channels := strings.Split(channel, ",") + // update mapping in the link link.Lock() - link.channels[channel] = time.Now() + for _, channel := range channels { + link.channels[channel] = time.Now() + } link.Unlock() + // this was an announcement not intended for anything + if sessionId == "listener" || sessionId == "" { + continue + } + // get the session that asked for the discovery s, exists := t.getSession(channel, sessionId) if exists { @@ -463,22 +487,46 @@ func (t *tun) listen(link *link) { } continue case "discover": - // looking for existing mapping - _, exists := t.getSession(channel, "listener") - if exists { - log.Debugf("Tunnel sending announce for discovery of channel %s", channel) - // send back the announcement - link.Send(&transport.Message{ - Header: map[string]string{ - "Micro-Tunnel": "announce", - "Micro-Tunnel-Id": t.id, - "Micro-Tunnel-Channel": channel, - "Micro-Tunnel-Session": sessionId, - "Micro-Tunnel-Link": link.id, - "Micro-Tunnel-Token": t.token, - }, - }) + // create the "announce" response message for a discover request + msg := &transport.Message{ + Header: map[string]string{ + "Micro-Tunnel": "announce", + "Micro-Tunnel-Id": t.id, + "Micro-Tunnel-Channel": channel, + "Micro-Tunnel-Session": sessionId, + "Micro-Tunnel-Link": link.id, + "Micro-Tunnel-Token": t.token, + }, } + + // if no channel is present we've been asked to discover all channels + if len(channel) == 0 { + // get the list of channels + t.RLock() + channels := t.listChannels() + t.RUnlock() + + // if there are no channels continue + if len(channels) == 0 { + continue + } + + // create a list of channels as comma separated list + list := strings.Join(channels, ",") + // set channels as header + msg.Header["Micro-Tunnel-Channel"] = list + } else { + // otherwise look for a single channel mapping + // looking for existing mapping as a listener + _, exists := t.getSession(channel, "listener") + if !exists { + continue + } + log.Debugf("Tunnel sending announce for discovery of channel %s", channel) + } + + // send back the announcement + link.Send(msg) continue default: // blackhole it @@ -728,6 +776,9 @@ func (t *tun) Connect() error { return err } + // request a discovery + t.discover() + // set as connected t.connected = true // create new close channel @@ -736,6 +787,19 @@ func (t *tun) Connect() error { return nil } +func (t *tun) discover() { + // send a discovery message to all links + for _, link := range t.links { + link.Send(&transport.Message{ + Header: map[string]string{ + "Micro-Tunnel": "discover", + "Micro-Tunnel-Id": t.id, + "Micro-Tunnel-Token": t.token, + }, + }) + } +} + func (t *tun) close() error { // close all the sessions for id, s := range t.sessions { @@ -757,7 +821,8 @@ func (t *tun) close() error { } // close the listener - return t.listener.Close() + //return t.listener.Close() + return nil } func (t *tun) Address() string { @@ -856,7 +921,7 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { t.RUnlock() // discovered so set the link if not multicast - // TODO: pick the link efficiently based + // TODO: pick the link efficiently based // on link status and saturation. if c.discovered && !c.multicast { // set the link @@ -866,6 +931,7 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { // shit fuck if !c.discovered { + // create a new discovery message for this channel msg := c.newMessage("discover") msg.broadcast = true msg.outbound = true @@ -874,14 +940,6 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { // send the discovery message t.send <- msg - // don't bother waiting around - // we're just going to assume things come online - if c.multicast { - c.discovered = true - c.accepted = true - return c, nil - } - select { case <-time.After(after()): return nil, ErrDialTimeout @@ -891,17 +949,33 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { } } + var err error + // wait for announce select { case msg := <-c.recv: if msg.typ != "announce" { - return nil, errors.New("failed to discover channel") + err = errors.New("failed to discover channel") } case <-time.After(after()): - return nil, ErrDialTimeout + err = ErrDialTimeout + } + + // don't both sending the error for multicast + // we're just going to assume things come online + if err == nil || c.multicast { + c.discovered = true + return c, nil + } + + // return the error if unicast + if err != nil { + return nil, err } } + // a unicast session so we call "open" and wait for an "accept" + // try to open the session err := c.Open() if err != nil { diff --git a/tunnel/listener.go b/tunnel/listener.go index e60ff396..ee394519 100644 --- a/tunnel/listener.go +++ b/tunnel/listener.go @@ -24,7 +24,7 @@ type tunListener struct { // periodically announce self func (t *tunListener) announce() { - tick := time.NewTicker(time.Minute) + tick := time.NewTicker(time.Second * 30) defer tick.Stop() // first announcement diff --git a/tunnel/session.go b/tunnel/session.go index 58246fdd..a4c7a2fe 100644 --- a/tunnel/session.go +++ b/tunnel/session.go @@ -180,6 +180,8 @@ func (s *session) Announce() error { msg := s.newMessage("announce") // we don't need an error back msg.errChan = nil + // announce to all + msg.broadcast = true // we don't need the link msg.link = "" From d198765c6c004a4f9a5440a23b9ec2fc4a389b9b Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Thu, 5 Sep 2019 15:23:19 +0100 Subject: [PATCH 72/93] Put back close of listener --- tunnel/default.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tunnel/default.go b/tunnel/default.go index 6b74bfa8..0cb35eba 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -821,8 +821,8 @@ func (t *tun) close() error { } // close the listener - //return t.listener.Close() - return nil + // this appears to be blocking + return t.listener.Close() } func (t *tun) Address() string { From 2522d8cb961ce34e3ca04fa9f484ef6556b14364 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Thu, 5 Sep 2019 16:04:44 +0100 Subject: [PATCH 73/93] Send solicit message when new neighbour is discovered --- network/default.go | 158 ++++++++++++++++++++++++++---------- network/proto/network.pb.go | 91 +++++++++++++++------ network/proto/network.proto | 6 ++ router/default.go | 15 ++-- router/proto/router.proto | 4 + router/service/service.go | 36 ++++++++ 6 files changed, 236 insertions(+), 74 deletions(-) diff --git a/network/default.go b/network/default.go index 94ee1d25..75731c43 100644 --- a/network/default.go +++ b/network/default.go @@ -275,12 +275,12 @@ func (n *network) acceptNetConn(l tunnel.Listener, recv chan *transport.Message) } // processNetChan processes messages received on NetworkChannel -func (n *network) processNetChan(l tunnel.Listener) { +func (n *network) processNetChan(client transport.Client, listener tunnel.Listener) { // receive network message queue recv := make(chan *transport.Message, 128) // accept NetworkChannel connections - go n.acceptNetConn(l, recv) + go n.acceptNetConn(listener, recv) for { select { @@ -319,6 +319,14 @@ func (n *network) processNetChan(l tunnel.Listener) { lastSeen: now, } n.Unlock() + // advertise the new neighbour to the network + if err := n.advertiseNeighbours(client); err != nil { + log.Debugf("Network failed to advertise neighbours: %v", err) + } + // advertise all the routes when a new node has connected + if err := n.Router.Solicit(); err != nil { + log.Debugf("Network failed to solicit routes: %s", err) + } case "neighbour": // mark the time the message has been received now := time.Now() @@ -341,6 +349,29 @@ func (n *network) processNetChan(l tunnel.Listener) { neighbours: make(map[string]*node), lastSeen: now, } + // send a solicit message when discovering a new node + node := &pbNet.Node{ + Id: n.options.Id, + Address: n.options.Address, + } + pbNetSolicit := &pbNet.Solicit{ + Node: node, + } + + if body, err := proto.Marshal(pbNetSolicit); err == nil { + // create transport message and chuck it down the pipe + m := transport.Message{ + Header: map[string]string{ + "Micro-Method": "solicit", + }, + Body: body, + } + + log.Debugf("Network sending solicit message from: %s", node.Id) + if err := client.Send(&m); err != nil { + log.Debugf("Network failed to send solicit messsage: %v", err) + } + } } // update lastSeen timestamp if n.neighbours[pbNetNeighbour.Node.Id].lastSeen.Before(now) { @@ -383,6 +414,52 @@ func (n *network) processNetChan(l tunnel.Listener) { } } +// advertiseNeighbours sends a neighbour message to the network +func (n *network) advertiseNeighbours(client transport.Client) error { + n.RLock() + nodes := make([]*pbNet.Node, len(n.neighbours)) + i := 0 + for id, _ := range n.neighbours { + nodes[i] = &pbNet.Node{ + Id: id, + Address: n.neighbours[id].address, + } + i++ + } + n.RUnlock() + + node := &pbNet.Node{ + Id: n.options.Id, + Address: n.options.Address, + } + pbNetNeighbour := &pbNet.Neighbour{ + Node: node, + Neighbours: nodes, + } + + body, err := proto.Marshal(pbNetNeighbour) + if err != nil { + // TODO: should we bail here? + log.Debugf("Network failed to marshal neighbour message: %v", err) + return err + } + // create transport message and chuck it down the pipe + m := transport.Message{ + Header: map[string]string{ + "Micro-Method": "neighbour", + }, + Body: body, + } + + log.Debugf("Network sending neighbour message from: %s", node.Id) + if err := client.Send(&m); err != nil { + log.Debugf("Network failed to send neighbour messsage: %v", err) + return err + } + + return nil +} + // announce announces node neighbourhood to the network func (n *network) announce(client transport.Client) { announce := time.NewTicker(AnnounceTime) @@ -393,44 +470,9 @@ func (n *network) announce(client transport.Client) { case <-n.closed: return case <-announce.C: - n.RLock() - nodes := make([]*pbNet.Node, len(n.neighbours)) - i := 0 - for id, _ := range n.neighbours { - nodes[i] = &pbNet.Node{ - Id: id, - Address: n.neighbours[id].address, - } - i++ - } - n.RUnlock() - - node := &pbNet.Node{ - Id: n.options.Id, - Address: n.options.Address, - } - pbNetNeighbour := &pbNet.Neighbour{ - Node: node, - Neighbours: nodes, - } - - body, err := proto.Marshal(pbNetNeighbour) - if err != nil { - // TODO: should we bail here? - log.Debugf("Network failed to marshal neighbour message: %v", err) - continue - } - // create transport message and chuck it down the pipe - m := transport.Message{ - Header: map[string]string{ - "Micro-Method": "neighbour", - }, - Body: body, - } - - log.Debugf("Network sending neighbour message from: %s", node.Id) - if err := client.Send(&m); err != nil { - log.Debugf("Network failed to send neighbour messsage: %v", err) + // advertise yourself to the network + if err := n.advertiseNeighbours(client); err != nil { + log.Debugf("Network failed to advertise neighbours: %v", err) continue } } @@ -565,12 +607,12 @@ func (n *network) setRouteMetric(route *router.Route) { } // processCtrlChan processes messages received on ControlChannel -func (n *network) processCtrlChan(l tunnel.Listener) { +func (n *network) processCtrlChan(client transport.Client, listener tunnel.Listener) { // receive control message queue recv := make(chan *transport.Message, 128) // accept ControlChannel cconnections - go n.acceptCtrlConn(l, recv) + go n.acceptCtrlConn(listener, recv) for { select { @@ -601,6 +643,29 @@ func (n *network) processCtrlChan(l tunnel.Listener) { lastSeen: now, } n.neighbours[pbRtrAdvert.Id] = advertNode + // send a solicit message when discovering a new node + node := &pbNet.Node{ + Id: n.options.Id, + Address: n.options.Address, + } + pbNetSolicit := &pbNet.Solicit{ + Node: node, + } + + if body, err := proto.Marshal(pbNetSolicit); err == nil { + // create transport message and chuck it down the pipe + m := transport.Message{ + Header: map[string]string{ + "Micro-Method": "solicit", + }, + Body: body, + } + + log.Debugf("Network sending solicit message from: %s", node.Id) + if err := client.Send(&m); err != nil { + log.Debugf("Network failed to send solicit messsage: %v", err) + } + } } n.RUnlock() @@ -657,6 +722,11 @@ func (n *network) processCtrlChan(l tunnel.Listener) { log.Debugf("Network failed to process advert %s: %v", advert.Id, err) continue } + case "solicit": + // advertise all the routes when a new node has connected + if err := n.Router.Solicit(); err != nil { + log.Debugf("Network failed to solicit routes: %s", err) + } } case <-n.closed: return @@ -828,11 +898,11 @@ func (n *network) Connect() error { // prune stale nodes go n.prune() // listen to network messages - go n.processNetChan(netListener) + go n.processNetChan(netClient, netListener) // advertise service routes go n.advertise(ctrlClient, advertChan) // accept and process routes - go n.processCtrlChan(ctrlListener) + go n.processCtrlChan(ctrlClient, ctrlListener) // set connected to true n.connected = true diff --git a/network/proto/network.pb.go b/network/proto/network.pb.go index 74124324..dc6c0d48 100644 --- a/network/proto/network.pb.go +++ b/network/proto/network.pb.go @@ -305,6 +305,47 @@ func (m *Close) GetNode() *Node { return nil } +// Solicit is sent when requesting route advertisement from the network nodes +type Solicit struct { + // network node + Node *Node `protobuf:"bytes,1,opt,name=node,proto3" json:"node,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Solicit) Reset() { *m = Solicit{} } +func (m *Solicit) String() string { return proto.CompactTextString(m) } +func (*Solicit) ProtoMessage() {} +func (*Solicit) Descriptor() ([]byte, []int) { + return fileDescriptor_8571034d60397816, []int{7} +} + +func (m *Solicit) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Solicit.Unmarshal(m, b) +} +func (m *Solicit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Solicit.Marshal(b, m, deterministic) +} +func (m *Solicit) XXX_Merge(src proto.Message) { + xxx_messageInfo_Solicit.Merge(m, src) +} +func (m *Solicit) XXX_Size() int { + return xxx_messageInfo_Solicit.Size(m) +} +func (m *Solicit) XXX_DiscardUnknown() { + xxx_messageInfo_Solicit.DiscardUnknown(m) +} + +var xxx_messageInfo_Solicit proto.InternalMessageInfo + +func (m *Solicit) GetNode() *Node { + if m != nil { + return m.Node + } + return nil +} + // Neighbour is used to nnounce node neighbourhood type Neighbour struct { // network node @@ -320,7 +361,7 @@ func (m *Neighbour) Reset() { *m = Neighbour{} } func (m *Neighbour) String() string { return proto.CompactTextString(m) } func (*Neighbour) ProtoMessage() {} func (*Neighbour) Descriptor() ([]byte, []int) { - return fileDescriptor_8571034d60397816, []int{7} + return fileDescriptor_8571034d60397816, []int{8} } func (m *Neighbour) XXX_Unmarshal(b []byte) error { @@ -363,33 +404,35 @@ func init() { proto.RegisterType((*Node)(nil), "go.micro.network.Node") proto.RegisterType((*Connect)(nil), "go.micro.network.Connect") proto.RegisterType((*Close)(nil), "go.micro.network.Close") + proto.RegisterType((*Solicit)(nil), "go.micro.network.Solicit") proto.RegisterType((*Neighbour)(nil), "go.micro.network.Neighbour") } func init() { proto.RegisterFile("network.proto", fileDescriptor_8571034d60397816) } var fileDescriptor_8571034d60397816 = []byte{ - // 348 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x52, 0x41, 0x4f, 0x32, 0x31, - 0x10, 0xfd, 0x58, 0xe0, 0x23, 0x0c, 0x62, 0x4c, 0xa3, 0x66, 0xb3, 0x06, 0x43, 0x7a, 0x40, 0x62, - 0x74, 0x31, 0x10, 0x3d, 0x79, 0x31, 0x1c, 0xbc, 0x10, 0x0e, 0x7b, 0xf4, 0xe6, 0xd2, 0x66, 0x69, - 0x94, 0x1d, 0x6c, 0xbb, 0xf1, 0x0f, 0xf8, 0xc3, 0x4d, 0xbb, 0x05, 0x97, 0x45, 0x30, 0xdc, 0xda, - 0x99, 0xf7, 0xe6, 0xcd, 0xe4, 0x3d, 0x68, 0xa7, 0x5c, 0x7f, 0xa2, 0x7c, 0x0b, 0x97, 0x12, 0x35, - 0x92, 0x93, 0x04, 0xc3, 0x85, 0x98, 0x49, 0x0c, 0x5d, 0x3d, 0x18, 0x25, 0x42, 0xcf, 0xb3, 0x38, - 0x9c, 0xe1, 0x62, 0x60, 0x3b, 0x83, 0x04, 0x6f, 0xf3, 0x87, 0xc4, 0x4c, 0x73, 0x39, 0xb0, 0x4c, - 0xf7, 0xc9, 0xc7, 0xd0, 0x36, 0xb4, 0x26, 0x42, 0xe9, 0x88, 0x7f, 0x64, 0x5c, 0x69, 0xfa, 0x08, - 0x47, 0xf9, 0x57, 0x2d, 0x31, 0x55, 0x9c, 0xdc, 0x40, 0x3d, 0x45, 0xc6, 0x95, 0x5f, 0xe9, 0x56, - 0xfb, 0xad, 0xe1, 0x79, 0x58, 0x56, 0x0d, 0xa7, 0xc8, 0x78, 0x94, 0x83, 0x68, 0x0f, 0x4e, 0xa7, - 0x5c, 0x24, 0xf3, 0x18, 0x33, 0x39, 0x47, 0x64, 0x6e, 0x2a, 0x39, 0x06, 0x4f, 0x30, 0xbf, 0xd2, - 0xad, 0xf4, 0x9b, 0x91, 0x27, 0x18, 0x7d, 0x81, 0xb3, 0x12, 0xce, 0xc9, 0x3d, 0x99, 0x2b, 0x0b, - 0x0d, 0xcb, 0x69, 0x0d, 0x2f, 0x7e, 0x91, 0x5d, 0xc1, 0xa2, 0x4d, 0x06, 0xbd, 0x83, 0x9a, 0x59, - 0xa9, 0xac, 0x49, 0x7c, 0x68, 0xbc, 0x32, 0x26, 0xb9, 0x52, 0xbe, 0x67, 0x8b, 0xab, 0x2f, 0xbd, - 0x87, 0xc6, 0x18, 0xd3, 0x94, 0xcf, 0x34, 0xb9, 0x86, 0x9a, 0xb9, 0xc4, 0xc9, 0xee, 0xba, 0xd6, - 0x62, 0xe8, 0x08, 0xea, 0xe3, 0x77, 0x54, 0xfc, 0x20, 0x12, 0x42, 0x73, 0xbd, 0xf9, 0x21, 0x44, - 0xf2, 0x00, 0xb0, 0xbe, 0x53, 0xf9, 0xd5, 0xbd, 0x6e, 0x14, 0x90, 0xc3, 0x2f, 0x0f, 0x1a, 0xd3, - 0xbc, 0x49, 0x9e, 0x01, 0xac, 0xb9, 0xc6, 0x7f, 0x45, 0xfc, 0x1f, 0xb6, 0x4b, 0x84, 0xb3, 0x2b, - 0xe8, 0x6c, 0x75, 0x8a, 0x99, 0xa0, 0xff, 0xc8, 0x04, 0x9a, 0xa6, 0x62, 0xc4, 0x14, 0xe9, 0x6c, - 0x6f, 0x51, 0x48, 0x54, 0x70, 0xb9, 0xab, 0xbd, 0x9e, 0x16, 0x43, 0x7b, 0x23, 0x0d, 0xa4, 0xb7, - 0xc7, 0xee, 0x42, 0xac, 0x82, 0xab, 0x3f, 0x71, 0x2b, 0x8d, 0xf8, 0xbf, 0x4d, 0xfb, 0xe8, 0x3b, - 0x00, 0x00, 0xff, 0xff, 0xfb, 0xa1, 0x6b, 0xb0, 0x45, 0x03, 0x00, 0x00, + // 360 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x53, 0x41, 0x4f, 0xf2, 0x40, + 0x10, 0xfd, 0x28, 0xf0, 0x35, 0x0c, 0x1f, 0x5f, 0xcc, 0x46, 0x4d, 0x53, 0x83, 0x21, 0x7b, 0x40, + 0x62, 0xb4, 0x18, 0x08, 0x9e, 0xbc, 0x18, 0x0e, 0x5e, 0x08, 0x87, 0x7a, 0xf3, 0x66, 0xbb, 0x9b, + 0xb2, 0x11, 0x3a, 0xb8, 0xbb, 0x8d, 0x7f, 0xc0, 0x1f, 0x6e, 0xba, 0x5d, 0xb0, 0x80, 0x60, 0xb8, + 0x75, 0xe6, 0xbd, 0x37, 0x6f, 0xa7, 0xfb, 0x16, 0x5a, 0x29, 0xd7, 0x1f, 0x28, 0xdf, 0x82, 0xa5, + 0x44, 0x8d, 0xe4, 0x24, 0xc1, 0x60, 0x21, 0x62, 0x89, 0x81, 0xed, 0xfb, 0xc3, 0x44, 0xe8, 0x59, + 0x16, 0x05, 0x31, 0x2e, 0xfa, 0x06, 0xe9, 0x27, 0x78, 0x5b, 0x7c, 0x48, 0xcc, 0x34, 0x97, 0x7d, + 0xa3, 0xb4, 0x45, 0x31, 0x86, 0xb6, 0xa0, 0x39, 0x11, 0x4a, 0x87, 0xfc, 0x3d, 0xe3, 0x4a, 0xd3, + 0x07, 0xf8, 0x57, 0x94, 0x6a, 0x89, 0xa9, 0xe2, 0xe4, 0x06, 0xea, 0x29, 0x32, 0xae, 0xbc, 0x4a, + 0xa7, 0xda, 0x6b, 0x0e, 0xce, 0x83, 0x6d, 0xd7, 0x60, 0x8a, 0x8c, 0x87, 0x05, 0x89, 0x76, 0xe1, + 0x74, 0xca, 0x45, 0x32, 0x8b, 0x30, 0x93, 0x33, 0x44, 0x66, 0xa7, 0x92, 0xff, 0xe0, 0x08, 0xe6, + 0x55, 0x3a, 0x95, 0x5e, 0x23, 0x74, 0x04, 0xa3, 0x2f, 0x70, 0xb6, 0xc5, 0xb3, 0x76, 0x8f, 0xf9, + 0x96, 0x25, 0xc0, 0x68, 0x9a, 0x83, 0x8b, 0x1f, 0x6c, 0x57, 0xb4, 0x70, 0x53, 0x41, 0xef, 0xa0, + 0x96, 0x1f, 0x69, 0xdb, 0x93, 0x78, 0xe0, 0xbe, 0x32, 0x26, 0xb9, 0x52, 0x9e, 0x63, 0x9a, 0xab, + 0x92, 0x8e, 0xc0, 0x1d, 0x63, 0x9a, 0xf2, 0x58, 0x93, 0x6b, 0xa8, 0xe5, 0x9b, 0x58, 0xdb, 0x7d, + 0xdb, 0x1a, 0x0e, 0x1d, 0x42, 0x7d, 0x3c, 0x47, 0xc5, 0x8f, 0x12, 0x8d, 0xc0, 0x7d, 0xc6, 0xb9, + 0x88, 0xc5, 0x71, 0x5e, 0x08, 0x8d, 0xf5, 0xc2, 0xc7, 0x08, 0xc9, 0x3d, 0xc0, 0xfa, 0xf7, 0x28, + 0xaf, 0x7a, 0xf0, 0x12, 0x4b, 0xcc, 0xc1, 0xa7, 0x03, 0xee, 0xb4, 0x00, 0xc9, 0x13, 0x80, 0xc9, + 0x44, 0x1e, 0x1b, 0x45, 0xbc, 0x6f, 0xb5, 0x0d, 0x92, 0xbd, 0x65, 0xbf, 0xbd, 0x83, 0x94, 0xa3, + 0x44, 0xff, 0x90, 0x09, 0x34, 0xf2, 0x4e, 0x6e, 0xa6, 0x48, 0x7b, 0xf7, 0x14, 0xa5, 0x20, 0xfa, + 0x97, 0xfb, 0xe0, 0xf5, 0xb4, 0x08, 0x5a, 0x1b, 0x21, 0x22, 0xdd, 0x03, 0x29, 0x29, 0xa5, 0xd1, + 0xbf, 0xfa, 0x95, 0xb7, 0xf2, 0x88, 0xfe, 0x9a, 0x47, 0x32, 0xfc, 0x0a, 0x00, 0x00, 0xff, 0xff, + 0x59, 0xcf, 0xab, 0xb5, 0x7c, 0x03, 0x00, 0x00, } diff --git a/network/proto/network.proto b/network/proto/network.proto index cfba6d04..6025d90b 100644 --- a/network/proto/network.proto +++ b/network/proto/network.proto @@ -49,6 +49,12 @@ message Close { Node node = 1; } +// Solicit is sent when requesting route advertisement from the network nodes +message Solicit { + // network node + Node node = 1; +} + // Neighbour is used to nnounce node neighbourhood message Neighbour { // network node diff --git a/router/default.go b/router/default.go index 1fa8161a..2294a149 100644 --- a/router/default.go +++ b/router/default.go @@ -120,6 +120,9 @@ func (r *router) manageRoute(route Route, action string) error { if err := r.table.Delete(route); err != nil && err != ErrRouteNotFound { return fmt.Errorf("failed deleting route for service %s: %s", route.Service, err) } + case "solicit": + // nothing to do here + return nil default: return fmt.Errorf("failed to manage route for service %s. Unknown action: %s", route.Service, action) } @@ -603,9 +606,9 @@ func (r *router) Advertise() (<-chan *Advert, error) { return advertChan, nil case Running: // list all the routes and pack them into even slice to advertise - events, err := r.flushRouteEvents() + events, err := r.flushRouteEvents(Create) if err != nil { - return nil, fmt.Errorf("failed to advertise routes: %s", err) + return nil, fmt.Errorf("failed to flush routes: %s", err) } // create event channels @@ -676,8 +679,8 @@ func (r *router) Process(a *Advert) error { return nil } -// flushRouteEvents lists all the routes and builds a slice of events -func (r *router) flushRouteEvents() ([]*Event, error) { +// flushRouteEvents returns a slice of events, one per each route in the routing table +func (r *router) flushRouteEvents(evType EventType) ([]*Event, error) { // list all routes routes, err := r.table.List() if err != nil { @@ -688,7 +691,7 @@ func (r *router) flushRouteEvents() ([]*Event, error) { events := make([]*Event, len(routes)) for i, route := range routes { event := &Event{ - Type: Create, + Type: evType, Timestamp: time.Now(), Route: route, } @@ -701,7 +704,7 @@ func (r *router) flushRouteEvents() ([]*Event, error) { // Solicit advertises all of its routes to the network // It returns error if the router fails to list the routes func (r *router) Solicit() error { - events, err := r.flushRouteEvents() + events, err := r.flushRouteEvents(Update) if err != nil { return fmt.Errorf("failed solicit routes: %s", err) } diff --git a/router/proto/router.proto b/router/proto/router.proto index 543fdd97..64445ff3 100644 --- a/router/proto/router.proto +++ b/router/proto/router.proto @@ -7,6 +7,7 @@ service Router { rpc Lookup(LookupRequest) returns (LookupResponse) {}; rpc Watch(WatchRequest) returns (stream Event) {}; rpc Advertise(Request) returns (stream Advert) {}; + rpc Solicit(Request) returns (Response) {}; rpc Process(Advert) returns (ProcessResponse) {}; rpc Status(Request) returns (StatusResponse) {}; } @@ -22,6 +23,9 @@ service Table { // Empty request message Request {} +// Empty response +message Response {} + // ListResponse is returned by List message ListResponse { repeated Route routes = 1; diff --git a/router/service/service.go b/router/service/service.go index 77619762..f3063546 100644 --- a/router/service/service.go +++ b/router/service/service.go @@ -220,6 +220,42 @@ func (s *svc) Process(advert *router.Advert) error { return nil } +// Solicit advertise all routes +func (s *svc) Solicit() error { + // list all the routes + routes, err := s.table.List() + if err != nil { + return err + } + + // build events to advertise + events := make([]*router.Event, len(routes)) + for i, _ := range events { + events[i] = &router.Event{ + Type: router.Update, + Timestamp: time.Now(), + Route: routes[i], + } + } + + advert := &router.Advert{ + Id: s.opts.Id, + Type: router.RouteUpdate, + Timestamp: time.Now(), + TTL: time.Duration(router.DefaultAdvertTTL), + Events: events, + } + + select { + case s.advertChan <- advert: + case <-s.exit: + close(s.advertChan) + return nil + } + + return nil +} + // Status returns router status func (s *svc) Status() router.Status { s.Lock() From 2310ee424ca89d09deebcad864199e90ca66b4c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E6=9C=9D=E6=99=96?= Date: Thu, 5 Sep 2019 23:52:54 +0800 Subject: [PATCH 74/93] Update README.zh-cn.md --- README.zh-cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.zh-cn.md b/README.zh-cn.md index b5599821..b770c8bd 100644 --- a/README.zh-cn.md +++ b/README.zh-cn.md @@ -32,5 +32,5 @@ Go Micro把分布式系统的各种细节抽象出来。下面是它的主要特 ## 快速上手 -更多关于架构、安装的资料可以查看[文档](https://micro.mu/docs/go-micro_cn.html)。 +更多关于架构、安装的资料可以查看[文档](https://micro.mu/docs/cn/)。 From 5ddfd911ba37abfcd17908e4ca2459db5142abe3 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Thu, 5 Sep 2019 17:18:16 +0100 Subject: [PATCH 75/93] Replace send message code by one network method --- network/default.go | 196 +++++++++++++++++---------------------------- 1 file changed, 75 insertions(+), 121 deletions(-) diff --git a/network/default.go b/network/default.go index 75731c43..4d8b1156 100644 --- a/network/default.go +++ b/network/default.go @@ -2,6 +2,7 @@ package network import ( "container/list" + "errors" "sync" "time" @@ -26,6 +27,10 @@ var ( ControlChannel = "control" // DefaultLink is default network link DefaultLink = "network" + // ErrMsgUnknown is returned when unknown message is attempted to send or receive + ErrMsgUnknown = errors.New("unknown message") + // ErrChannelUnknown is returned when attempting to send or received on unknown channel + ErrChannelUnknown = errors.New("unknown channel") ) // node is network node @@ -319,8 +324,8 @@ func (n *network) processNetChan(client transport.Client, listener tunnel.Listen lastSeen: now, } n.Unlock() - // advertise the new neighbour to the network - if err := n.advertiseNeighbours(client); err != nil { + // advertise yourself to the network + if err := n.sendMsg("neighbour", NetworkChannel); err != nil { log.Debugf("Network failed to advertise neighbours: %v", err) } // advertise all the routes when a new node has connected @@ -342,43 +347,21 @@ func (n *network) processNetChan(client transport.Client, listener tunnel.Listen n.Lock() log.Debugf("Network received neighbour message from: %s", pbNetNeighbour.Node.Id) // only add the neighbour if it is NOT already in node's list of neighbours - if _, ok := n.neighbours[pbNetNeighbour.Node.Id]; !ok { + _, exists := n.neighbours[pbNetNeighbour.Node.Id] + if !exists { n.neighbours[pbNetNeighbour.Node.Id] = &node{ id: pbNetNeighbour.Node.Id, address: pbNetNeighbour.Node.Address, neighbours: make(map[string]*node), lastSeen: now, } - // send a solicit message when discovering a new node - node := &pbNet.Node{ - Id: n.options.Id, - Address: n.options.Address, - } - pbNetSolicit := &pbNet.Solicit{ - Node: node, - } - - if body, err := proto.Marshal(pbNetSolicit); err == nil { - // create transport message and chuck it down the pipe - m := transport.Message{ - Header: map[string]string{ - "Micro-Method": "solicit", - }, - Body: body, - } - - log.Debugf("Network sending solicit message from: %s", node.Id) - if err := client.Send(&m); err != nil { - log.Debugf("Network failed to send solicit messsage: %v", err) - } - } } // update lastSeen timestamp if n.neighbours[pbNetNeighbour.Node.Id].lastSeen.Before(now) { n.neighbours[pbNetNeighbour.Node.Id].lastSeen = now } // update/store the neighbour node neighbours - // NOTE: * we dont update lastSeen time for the neighbours of the neighbour + // NOTE: * we do NOT update lastSeen time for the neighbours of the neighbour // * even though we are NOT interested in neighbours of neighbours here // we still allocate the map of neighbours for each of them for _, pbNeighbour := range pbNetNeighbour.Neighbours { @@ -390,6 +373,13 @@ func (n *network) processNetChan(client transport.Client, listener tunnel.Listen n.neighbours[pbNetNeighbour.Node.Id].neighbours[neighbourNode.id] = neighbourNode } n.Unlock() + // send a solicit message when discovering a new node + // NOTE: we need to send the solicit message here after the Lock is released as sendMsg locs + if !exists { + if err := n.sendMsg("solicit", NetworkChannel); err != nil { + log.Debugf("Network failed to send solicit message: %s", err) + } + } case "close": pbNetClose := &pbNet.Close{} if err := proto.Unmarshal(m.Body, pbNetClose); err != nil { @@ -414,46 +404,70 @@ func (n *network) processNetChan(client transport.Client, listener tunnel.Listen } } -// advertiseNeighbours sends a neighbour message to the network -func (n *network) advertiseNeighbours(client transport.Client) error { - n.RLock() - nodes := make([]*pbNet.Node, len(n.neighbours)) - i := 0 - for id, _ := range n.neighbours { - nodes[i] = &pbNet.Node{ - Id: id, - Address: n.neighbours[id].address, - } - i++ - } - n.RUnlock() - +// sendMsg sends a message to the tunnel channel +func (n *network) sendMsg(msgType string, channel string) error { node := &pbNet.Node{ Id: n.options.Id, Address: n.options.Address, } - pbNetNeighbour := &pbNet.Neighbour{ - Node: node, - Neighbours: nodes, + + var protoMsg proto.Message + + switch msgType { + case "connect": + protoMsg = &pbNet.Connect{ + Node: node, + } + case "close": + protoMsg = &pbNet.Close{ + Node: node, + } + case "solicit": + protoMsg = &pbNet.Solicit{ + Node: node, + } + case "neighbour": + n.RLock() + nodes := make([]*pbNet.Node, len(n.neighbours)) + i := 0 + for id, _ := range n.neighbours { + nodes[i] = &pbNet.Node{ + Id: id, + Address: n.neighbours[id].address, + } + i++ + } + n.RUnlock() + protoMsg = &pbNet.Neighbour{ + Node: node, + Neighbours: nodes, + } + default: + return ErrMsgUnknown } - body, err := proto.Marshal(pbNetNeighbour) + body, err := proto.Marshal(protoMsg) if err != nil { - // TODO: should we bail here? - log.Debugf("Network failed to marshal neighbour message: %v", err) return err } // create transport message and chuck it down the pipe m := transport.Message{ Header: map[string]string{ - "Micro-Method": "neighbour", + "Micro-Method": msgType, }, Body: body, } - log.Debugf("Network sending neighbour message from: %s", node.Id) + n.RLock() + client, ok := n.tunClient[channel] + if !ok { + n.RUnlock() + return ErrChannelUnknown + } + n.RUnlock() + + log.Debugf("Network sending %s message from: %s", msgType, node.Id) if err := client.Send(&m); err != nil { - log.Debugf("Network failed to send neighbour messsage: %v", err) return err } @@ -471,7 +485,7 @@ func (n *network) announce(client transport.Client) { return case <-announce.C: // advertise yourself to the network - if err := n.advertiseNeighbours(client); err != nil { + if err := n.sendMsg("neighbour", NetworkChannel); err != nil { log.Debugf("Network failed to advertise neighbours: %v", err) continue } @@ -644,27 +658,8 @@ func (n *network) processCtrlChan(client transport.Client, listener tunnel.Liste } n.neighbours[pbRtrAdvert.Id] = advertNode // send a solicit message when discovering a new node - node := &pbNet.Node{ - Id: n.options.Id, - Address: n.options.Address, - } - pbNetSolicit := &pbNet.Solicit{ - Node: node, - } - - if body, err := proto.Marshal(pbNetSolicit); err == nil { - // create transport message and chuck it down the pipe - m := transport.Message{ - Header: map[string]string{ - "Micro-Method": "solicit", - }, - Body: body, - } - - log.Debugf("Network sending solicit message from: %s", node.Id) - if err := client.Send(&m); err != nil { - log.Debugf("Network failed to send solicit messsage: %v", err) - } + if err := n.sendMsg("solicit", NetworkChannel); err != nil { + log.Debugf("Network failed to send solicit message: %s", err) } } n.RUnlock() @@ -794,8 +789,6 @@ func (n *network) advertise(client transport.Client, advertChan <-chan *router.A // Connect connects the network func (n *network) Connect() error { n.Lock() - defer n.Unlock() - // return if already connected if n.connected { return nil @@ -863,32 +856,14 @@ func (n *network) Connect() error { if err := n.server.Start(); err != nil { return err } + n.Unlock() // send connect message to NetworkChannel // NOTE: in theory we could do this as soon as // Dial to NetworkChannel succeeds, but instead // we initialize all other node resources first - node := &pbNet.Node{ - Id: n.options.Id, - Address: n.options.Address, - } - pbNetConnect := &pbNet.Connect{ - Node: node, - } - - // only proceed with sending to NetworkChannel if marshal succeeds - if body, err := proto.Marshal(pbNetConnect); err == nil { - m := transport.Message{ - Header: map[string]string{ - "Micro-Method": "connect", - }, - Body: body, - } - - log.Debugf("Network sending connect message: %s", node.Id) - if err := netClient.Send(&m); err != nil { - log.Debugf("Network failed to send connect messsage: %v", err) - } + if err := n.sendMsg("connect", NetworkChannel); err != nil { + log.Debugf("Network failed to send connect message: %s", err) } // go resolving network nodes @@ -904,8 +879,9 @@ func (n *network) Connect() error { // accept and process routes go n.processCtrlChan(ctrlClient, ctrlListener) - // set connected to true + n.Lock() n.connected = true + n.Unlock() return nil } @@ -982,31 +958,9 @@ func (n *network) Close() error { return nil default: // send close message only if we managed to connect to NetworkChannel - if netClient, ok := n.tunClient[NetworkChannel]; ok { - // send connect message to NetworkChannel - node := &pbNet.Node{ - Id: n.options.Id, - Address: n.options.Address, - } - pbNetClose := &pbNet.Close{ - Node: node, - } - - // only proceed with sending to NetworkChannel if marshal succeeds - if body, err := proto.Marshal(pbNetClose); err == nil { - // create transport message and chuck it down the pipe - m := transport.Message{ - Header: map[string]string{ - "Micro-Method": "close", - }, - Body: body, - } - - log.Debugf("Network sending close message from: %s", node.Id) - if err := netClient.Send(&m); err != nil { - log.Debugf("Network failed to send close messsage: %v", err) - } - } + log.Debugf("Sending close message from: %s", n.options.Id) + if err := n.sendMsg("close", NetworkChannel); err != nil { + log.Debugf("Network failed to send close message: %s", err) } // TODO: send close message to the network channel close(n.closed) From 1527a842976c69724a261ddd6e37795d922c9b9f Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Thu, 5 Sep 2019 17:40:41 +0100 Subject: [PATCH 76/93] Shorten multicast discovery --- tunnel/default.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tunnel/default.go b/tunnel/default.go index 0cb35eba..1170a00d 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -951,27 +951,39 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { var err error + // set a dialTimeout + dialTimeout := after() + + // set a shorter delay for multicast + if c.multicast { + // shorten this + dialTimeout = time.Millisecond * 500 + } + // wait for announce select { case msg := <-c.recv: if msg.typ != "announce" { err = errors.New("failed to discover channel") } - case <-time.After(after()): + case <-time.After(dialTimeout): err = ErrDialTimeout } - // don't both sending the error for multicast - // we're just going to assume things come online - if err == nil || c.multicast { + // if its multicast just go ahead because this is best effort + if c.multicast { c.discovered = true + c.accepted = true return c, nil } - // return the error if unicast + // otherwise return an error if err != nil { return nil, err } + + // set discovered to true + c.discovered = true } // a unicast session so we call "open" and wait for an "accept" From b01c8e06e08ce1dc22a3680e3f3a02efdf02b6f9 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Thu, 5 Sep 2019 17:43:59 +0100 Subject: [PATCH 77/93] Update error name to ErrClientNotFound --- network/default.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/network/default.go b/network/default.go index 4d8b1156..3da04650 100644 --- a/network/default.go +++ b/network/default.go @@ -29,8 +29,8 @@ var ( DefaultLink = "network" // ErrMsgUnknown is returned when unknown message is attempted to send or receive ErrMsgUnknown = errors.New("unknown message") - // ErrChannelUnknown is returned when attempting to send or received on unknown channel - ErrChannelUnknown = errors.New("unknown channel") + // ErrClientNotFound is returned when client for tunnel channel could not be found + ErrClientNotFound = errors.New("client not found") ) // node is network node @@ -462,7 +462,7 @@ func (n *network) sendMsg(msgType string, channel string) error { client, ok := n.tunClient[channel] if !ok { n.RUnlock() - return ErrChannelUnknown + return ErrClientNotFound } n.RUnlock() From ec354934e3f7bc93075b0e73fb2e9996ad4de02d Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Thu, 5 Sep 2019 17:44:47 +0100 Subject: [PATCH 78/93] Move Errors to separate init block --- network/default.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/network/default.go b/network/default.go index 3da04650..e0123c3f 100644 --- a/network/default.go +++ b/network/default.go @@ -27,6 +27,9 @@ var ( ControlChannel = "control" // DefaultLink is default network link DefaultLink = "network" +) + +var ( // ErrMsgUnknown is returned when unknown message is attempted to send or receive ErrMsgUnknown = errors.New("unknown message") // ErrClientNotFound is returned when client for tunnel channel could not be found From dddfb6f878a0856aba3fd22b91943ebe49fb3655 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Thu, 5 Sep 2019 17:59:14 +0100 Subject: [PATCH 79/93] Fixed typos and simplified map iteration --- network/default.go | 6 +++--- router/router.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/network/default.go b/network/default.go index e0123c3f..674bba3c 100644 --- a/network/default.go +++ b/network/default.go @@ -377,7 +377,7 @@ func (n *network) processNetChan(client transport.Client, listener tunnel.Listen } n.Unlock() // send a solicit message when discovering a new node - // NOTE: we need to send the solicit message here after the Lock is released as sendMsg locs + // NOTE: we need to send the solicit message here after the Lock is released as sendMsg locks, too if !exists { if err := n.sendMsg("solicit", NetworkChannel); err != nil { log.Debugf("Network failed to send solicit message: %s", err) @@ -433,7 +433,7 @@ func (n *network) sendMsg(msgType string, channel string) error { n.RLock() nodes := make([]*pbNet.Node, len(n.neighbours)) i := 0 - for id, _ := range n.neighbours { + for id := range n.neighbours { nodes[i] = &pbNet.Node{ Id: id, Address: n.neighbours[id].address, @@ -609,7 +609,7 @@ func (n *network) setRouteMetric(route *router.Route) { // check if the route origin is the neighbour of our neighbour for _, node := range n.neighbours { - for id, _ := range node.neighbours { + for id := range node.neighbours { if route.Router == id { route.Metric = 100 n.RUnlock() diff --git a/router/router.go b/router/router.go index 224ef124..3e8f00cd 100644 --- a/router/router.go +++ b/router/router.go @@ -28,7 +28,7 @@ type Router interface { Advertise() (<-chan *Advert, error) // Process processes incoming adverts Process(*Advert) error - // Solicit advertises the whole routing table ot the network + // Solicit advertises the whole routing table to the network Solicit() error // Lookup queries routes in the routing table Lookup(Query) ([]Route, error) From ed1faa7a5ccf66541f7ed5f4eeccf8047185b241 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Thu, 5 Sep 2019 18:13:02 +0100 Subject: [PATCH 80/93] Add a discover ticker, announce on connect and refactor --- tunnel/default.go | 138 +++++++++++++++++++++++++++------------------- tunnel/tunnel.go | 6 +- 2 files changed, 85 insertions(+), 59 deletions(-) diff --git a/tunnel/default.go b/tunnel/default.go index 1170a00d..7d265544 100644 --- a/tunnel/default.go +++ b/tunnel/default.go @@ -13,6 +13,8 @@ import ( ) var ( + // DiscoverTime sets the time at which we fire discover messages + DiscoverTime = 60 * time.Second // KeepAliveTime defines time interval we send keepalive messages to outbound links KeepAliveTime = 30 * time.Second // ReconnectTime defines time interval we periodically attempt to reconnect dead links @@ -144,6 +146,52 @@ func (t *tun) newSessionId() string { return uuid.New().String() } +func (t *tun) announce(channel, session string, link *link) { + // create the "announce" response message for a discover request + msg := &transport.Message{ + Header: map[string]string{ + "Micro-Tunnel": "announce", + "Micro-Tunnel-Id": t.id, + "Micro-Tunnel-Channel": channel, + "Micro-Tunnel-Session": session, + "Micro-Tunnel-Link": link.id, + "Micro-Tunnel-Token": t.token, + }, + } + + // if no channel is present we've been asked to discover all channels + if len(channel) == 0 { + // get the list of channels + t.RLock() + channels := t.listChannels() + t.RUnlock() + + // if there are no channels continue + if len(channels) == 0 { + return + } + + // create a list of channels as comma separated list + channel = strings.Join(channels, ",") + // set channels as header + msg.Header["Micro-Tunnel-Channel"] = channel + } else { + // otherwise look for a single channel mapping + // looking for existing mapping as a listener + _, exists := t.getSession(channel, "listener") + if !exists { + return + } + } + + log.Debugf("Tunnel sending announce for discovery of channel(s) %s", channel) + + // send back the announcement + if err := link.Send(msg); err != nil { + log.Debugf("Tunnel failed to send announcement for channel(s) %s message: %v", channel, err) + } +} + // monitor monitors outbound links and attempts to reconnect to the failed ones func (t *tun) monitor() { reconnect := time.NewTicker(ReconnectTime) @@ -398,6 +446,8 @@ func (t *tun) listen(link *link) { t.links[link.Remote()] = link t.Unlock() + // send back a discovery + go t.announce("", "", link) // nothing more to do continue case "close": @@ -454,6 +504,7 @@ func (t *tun) listen(link *link) { log.Debugf("Received %+v from %s", msg, link.Remote()) // an announcement of a channel listener case "announce": + // process the announcement channels := strings.Split(channel, ",") // update mapping in the link @@ -487,46 +538,8 @@ func (t *tun) listen(link *link) { } continue case "discover": - // create the "announce" response message for a discover request - msg := &transport.Message{ - Header: map[string]string{ - "Micro-Tunnel": "announce", - "Micro-Tunnel-Id": t.id, - "Micro-Tunnel-Channel": channel, - "Micro-Tunnel-Session": sessionId, - "Micro-Tunnel-Link": link.id, - "Micro-Tunnel-Token": t.token, - }, - } - - // if no channel is present we've been asked to discover all channels - if len(channel) == 0 { - // get the list of channels - t.RLock() - channels := t.listChannels() - t.RUnlock() - - // if there are no channels continue - if len(channels) == 0 { - continue - } - - // create a list of channels as comma separated list - list := strings.Join(channels, ",") - // set channels as header - msg.Header["Micro-Tunnel-Channel"] = list - } else { - // otherwise look for a single channel mapping - // looking for existing mapping as a listener - _, exists := t.getSession(channel, "listener") - if !exists { - continue - } - log.Debugf("Tunnel sending announce for discovery of channel %s", channel) - } - - // send back the announcement - link.Send(msg) + // send back an announcement + go t.announce(channel, sessionId, link) continue default: // blackhole it @@ -635,6 +648,30 @@ func (t *tun) listen(link *link) { } } +// discover sends channel discover requests periodically +func (t *tun) discover(link *link) { + tick := time.NewTicker(DiscoverTime) + defer tick.Stop() + + for { + select { + case <-tick.C: + // send a discovery message to all links + if err := link.Send(&transport.Message{ + Header: map[string]string{ + "Micro-Tunnel": "discover", + "Micro-Tunnel-Id": t.id, + "Micro-Tunnel-Token": t.token, + }, + }); err != nil { + log.Debugf("Tunnel failed to send discover to link %s: %v", link.id, err) + } + case <-t.closed: + return + } + } +} + // keepalive periodically sends keepalive messages to link func (t *tun) keepalive(link *link) { keepalive := time.NewTicker(KeepAliveTime) @@ -698,6 +735,9 @@ func (t *tun) setupLink(node string) (*link, error) { // start keepalive monitor go t.keepalive(link) + // discover things on the remote side + go t.discover(link) + return link, nil } @@ -776,9 +816,6 @@ func (t *tun) Connect() error { return err } - // request a discovery - t.discover() - // set as connected t.connected = true // create new close channel @@ -787,19 +824,6 @@ func (t *tun) Connect() error { return nil } -func (t *tun) discover() { - // send a discovery message to all links - for _, link := range t.links { - link.Send(&transport.Message{ - Header: map[string]string{ - "Micro-Tunnel": "discover", - "Micro-Tunnel-Id": t.id, - "Micro-Tunnel-Token": t.token, - }, - }) - } -} - func (t *tun) close() error { // close all the sessions for id, s := range t.sessions { @@ -964,7 +988,7 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) { select { case msg := <-c.recv: if msg.typ != "announce" { - err = errors.New("failed to discover channel") + err = ErrDiscoverChan } case <-time.After(dialTimeout): err = ErrDialTimeout diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 7c9e2afc..14604215 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -9,10 +9,12 @@ import ( ) var ( - // ErrDialTimeout is returned by a call to Dial where the timeout occurs - ErrDialTimeout = errors.New("dial timeout") // DefaultDialTimeout is the dial timeout if none is specified DefaultDialTimeout = time.Second * 5 + // ErrDialTimeout is returned by a call to Dial where the timeout occurs + ErrDialTimeout = errors.New("dial timeout") + // ErrDiscoverChan is returned when we failed to receive the "announce" back from a discovery + ErrDiscoverChan = errors.New("failed to discover channel") ) // Tunnel creates a gre tunnel on top of the go-micro/transport. From bb595c85b2bb93274717337b6251a0d8c99e2538 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Thu, 5 Sep 2019 19:05:47 +0100 Subject: [PATCH 81/93] Lets make advert channel buffered so we don't lose adverts --- router/default.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/router/default.go b/router/default.go index 2294a149..cfc30548 100644 --- a/router/default.go +++ b/router/default.go @@ -10,6 +10,7 @@ import ( "github.com/google/uuid" "github.com/micro/go-micro/registry" + "github.com/micro/go-micro/util/log" ) const ( @@ -289,6 +290,7 @@ func (r *router) publishAdvert(advType AdvertType, events []*Event) { Events: events, } + log.Debugf("Router publishing advert; %+v", a) r.RLock() for _, sub := range r.subscribers { // check the exit chan first @@ -601,7 +603,7 @@ func (r *router) Advertise() (<-chan *Advert, error) { switch r.status.Code { case Advertising: - advertChan := make(chan *Advert) + advertChan := make(chan *Advert, 128) r.subscribers[uuid.New().String()] = advertChan return advertChan, nil case Running: @@ -641,7 +643,7 @@ func (r *router) Advertise() (<-chan *Advert, error) { r.status = Status{Code: Advertising, Error: nil} // create advert channel - advertChan := make(chan *Advert) + advertChan := make(chan *Advert, 128) r.subscribers[uuid.New().String()] = advertChan return advertChan, nil From dafbacbdcb18c0846a70d80b4894b4c55b884f37 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Fri, 6 Sep 2019 15:05:58 +0100 Subject: [PATCH 82/93] Properly handle the list of the nodes. Send solicit on ControlChannel --- network/default.go | 26 +++++++++++++++++++++----- router/proto/router.proto | 2 ++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/network/default.go b/network/default.go index 674bba3c..a2d4f862 100644 --- a/network/default.go +++ b/network/default.go @@ -352,6 +352,7 @@ func (n *network) processNetChan(client transport.Client, listener tunnel.Listen // only add the neighbour if it is NOT already in node's list of neighbours _, exists := n.neighbours[pbNetNeighbour.Node.Id] if !exists { + log.Debugf("Network neighbour message node exists: %s", pbNetNeighbour.Node.Id) n.neighbours[pbNetNeighbour.Node.Id] = &node{ id: pbNetNeighbour.Node.Id, address: pbNetNeighbour.Node.Address, @@ -373,13 +374,13 @@ func (n *network) processNetChan(client transport.Client, listener tunnel.Listen address: pbNeighbour.Address, neighbours: make(map[string]*node), } - n.neighbours[pbNetNeighbour.Node.Id].neighbours[neighbourNode.id] = neighbourNode + n.neighbours[pbNetNeighbour.Node.Id].neighbours[pbNeighbour.Id] = neighbourNode } n.Unlock() // send a solicit message when discovering a new node // NOTE: we need to send the solicit message here after the Lock is released as sendMsg locks, too if !exists { - if err := n.sendMsg("solicit", NetworkChannel); err != nil { + if err := n.sendMsg("solicit", ControlChannel); err != nil { log.Debugf("Network failed to send solicit message: %s", err) } } @@ -696,8 +697,10 @@ func (n *network) processCtrlChan(client transport.Client, listener tunnel.Liste } // set the route metric n.setRouteMetric(&route) + log.Debugf("Network node %s metric: %d", route.Router, route.Metric) // throw away metric bigger than 1000 if route.Metric > 1000 { + log.Debugf("Network dropping node: %s", route.Router) continue } // create router event @@ -721,6 +724,16 @@ func (n *network) processCtrlChan(client transport.Client, listener tunnel.Liste continue } case "solicit": + pbNetSolicit := &pbNet.Solicit{} + if err := proto.Unmarshal(m.Body, pbNetSolicit); err != nil { + log.Debugf("Network fail to unmarshal solicit message: %v", err) + continue + } + log.Debugf("Network received solicit message from: %s", pbNetSolicit.Node.Id) + // don't process your own messages + if pbNetSolicit.Node.Id == n.options.Id { + continue + } // advertise all the routes when a new node has connected if err := n.Router.Solicit(); err != nil { log.Debugf("Network failed to solicit routes: %s", err) @@ -907,8 +920,9 @@ func (n *network) Nodes() []Node { visited[n.node.id] = n.node // keep iterating over the queue until its empty - for qnode := queue.Front(); qnode != nil; qnode = qnode.Next() { - queue.Remove(qnode) + for queue.Len() > 0 { + // pop the node from the front of the queue + qnode := queue.Front() // iterate through all of its neighbours // mark the visited nodes; enqueue the non-visted for id, node := range qnode.Value.(*node).neighbours { @@ -917,11 +931,13 @@ func (n *network) Nodes() []Node { queue.PushBack(node) } } + // remove the node from the queue + queue.Remove(qnode) } var nodes []Node // collect all the nodes and return them - for _, node := range visited { + for id, node := range visited { nodes = append(nodes, node) } diff --git a/router/proto/router.proto b/router/proto/router.proto index 64445ff3..9c2ebed4 100644 --- a/router/proto/router.proto +++ b/router/proto/router.proto @@ -41,10 +41,12 @@ message LookupResponse { repeated Route routes = 1; } +// QueryRequest queries Table for Routes message QueryRequest{ Query query = 1; } +// QueryResponse is returned by Query message QueryResponse { repeated Route routes = 1; } From 7971b1b7f918ba09b2c32d872ee73e058e1e46a0 Mon Sep 17 00:00:00 2001 From: Milos Gajdos Date: Fri, 6 Sep 2019 15:12:23 +0100 Subject: [PATCH 83/93] Remove debug logs --- network/default.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/network/default.go b/network/default.go index a2d4f862..31fd57f2 100644 --- a/network/default.go +++ b/network/default.go @@ -352,7 +352,6 @@ func (n *network) processNetChan(client transport.Client, listener tunnel.Listen // only add the neighbour if it is NOT already in node's list of neighbours _, exists := n.neighbours[pbNetNeighbour.Node.Id] if !exists { - log.Debugf("Network neighbour message node exists: %s", pbNetNeighbour.Node.Id) n.neighbours[pbNetNeighbour.Node.Id] = &node{ id: pbNetNeighbour.Node.Id, address: pbNetNeighbour.Node.Address, @@ -697,10 +696,9 @@ func (n *network) processCtrlChan(client transport.Client, listener tunnel.Liste } // set the route metric n.setRouteMetric(&route) - log.Debugf("Network node %s metric: %d", route.Router, route.Metric) // throw away metric bigger than 1000 if route.Metric > 1000 { - log.Debugf("Network dropping node: %s", route.Router) + log.Debugf("Network route metric %d dropping node: %s", route.Metric, route.Router) continue } // create router event @@ -937,7 +935,7 @@ func (n *network) Nodes() []Node { var nodes []Node // collect all the nodes and return them - for id, node := range visited { + for _, node := range visited { nodes = append(nodes, node) } From e8aaca27d3f6cb4c36c52ca55a0776bfb716e07e Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Fri, 6 Sep 2019 16:57:17 +0100 Subject: [PATCH 84/93] unlock before sending the message to avoid deadlock --- network/default.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/network/default.go b/network/default.go index 31fd57f2..23c924f6 100644 --- a/network/default.go +++ b/network/default.go @@ -963,26 +963,32 @@ func (n *network) close() error { // Close closes network connection func (n *network) Close() error { + // lock this operation n.Lock() - defer n.Unlock() if !n.connected { + n.Unlock() return nil } select { case <-n.closed: + n.Unlock() return nil default: + // TODO: send close message to the network channel + close(n.closed) + // set connected to false + n.connected = false + + // unlock the lock otherwise we'll deadlock sending the close + n.Unlock() + // send close message only if we managed to connect to NetworkChannel log.Debugf("Sending close message from: %s", n.options.Id) if err := n.sendMsg("close", NetworkChannel); err != nil { log.Debugf("Network failed to send close message: %s", err) } - // TODO: send close message to the network channel - close(n.closed) - // set connected to false - n.connected = false } return n.close() From c669a2b155b7f30c15fe41872449899a2e7a3a5d Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Mon, 9 Sep 2019 05:11:25 -0700 Subject: [PATCH 85/93] Use .micro domain for mdns --- registry/mdns/mdns.go | 12 +++++++++++ registry/mdns_registry.go | 44 ++++++++++++++++++++++++++++++++------- registry/mdns_watcher.go | 9 +++++--- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/registry/mdns/mdns.go b/registry/mdns/mdns.go index 6739c694..f84e8961 100644 --- a/registry/mdns/mdns.go +++ b/registry/mdns/mdns.go @@ -2,6 +2,8 @@ package mdns import ( + "context" + "github.com/micro/go-micro/registry" ) @@ -9,3 +11,13 @@ import ( func NewRegistry(opts ...registry.Option) registry.Registry { return registry.NewRegistry(opts...) } + +// Domain sets the mdnsDomain +func Domain(d string) registry.Option { + return func(o *registry.Options) { + if o.Context == nil { + o.Context = context.Background() + } + o.Context = context.WithValue(o.Context, "mdns.domain", d) + } +} diff --git a/registry/mdns_registry.go b/registry/mdns_registry.go index 8e68948a..039710a6 100644 --- a/registry/mdns_registry.go +++ b/registry/mdns_registry.go @@ -14,6 +14,11 @@ import ( hash "github.com/mitchellh/hashstructure" ) +var ( + // use a .micro domain rather than .local + mdnsDomain = "micro" +) + type mdnsTxt struct { Service string Version string @@ -29,6 +34,8 @@ type mdnsEntry struct { type mdnsRegistry struct { opts Options + // the mdns domain + domain string sync.Mutex services map[string][]*mdnsEntry @@ -36,11 +43,25 @@ type mdnsRegistry struct { func newRegistry(opts ...Option) Registry { options := Options{ + Context: context.Background(), Timeout: time.Millisecond * 100, } + for _, o := range opts { + o(&options) + } + + // set the domain + domain := mdnsDomain + + d, ok := options.Context.Value("mdns.domain").(string) + if ok { + domain = d + } + return &mdnsRegistry{ opts: options, + domain: domain, services: make(map[string][]*mdnsEntry), } } @@ -66,7 +87,7 @@ func (m *mdnsRegistry) Register(service *Service, opts ...RegisterOption) error s, err := mdns.NewMDNSService( service.Name, "_services", - "", + m.domain+".", "", 9999, []net.IP{net.ParseIP("0.0.0.0")}, @@ -141,7 +162,7 @@ func (m *mdnsRegistry) Register(service *Service, opts ...RegisterOption) error s, err := mdns.NewMDNSService( node.Id, service.Name, - "", + m.domain+".", "", port, []net.IP{net.ParseIP(host)}, @@ -214,6 +235,8 @@ func (m *mdnsRegistry) GetService(service string) ([]*Service, error) { p.Context, _ = context.WithTimeout(context.Background(), m.opts.Timeout) // set entries channel p.Entries = entries + // set the domain + p.Domain = m.domain go func() { for { @@ -223,7 +246,9 @@ func (m *mdnsRegistry) GetService(service string) ([]*Service, error) { if p.Service == "_services" { continue } - + if p.Domain != m.domain { + continue + } if e.TTL == 0 { continue } @@ -288,6 +313,8 @@ func (m *mdnsRegistry) ListServices() ([]*Service, error) { p.Context, _ = context.WithTimeout(context.Background(), m.opts.Timeout) // set entries channel p.Entries = entries + // set domain + p.Domain = m.domain var services []*Service @@ -298,7 +325,9 @@ func (m *mdnsRegistry) ListServices() ([]*Service, error) { if e.TTL == 0 { continue } - + if !strings.HasSuffix(e.Name, p.Domain+".") { + continue + } name := strings.TrimSuffix(e.Name, "."+p.Service+"."+p.Domain+".") if !serviceMap[name] { serviceMap[name] = true @@ -329,9 +358,10 @@ func (m *mdnsRegistry) Watch(opts ...WatchOption) (Watcher, error) { } md := &mdnsWatcher{ - wo: wo, - ch: make(chan *mdns.ServiceEntry, 32), - exit: make(chan struct{}), + wo: wo, + ch: make(chan *mdns.ServiceEntry, 32), + exit: make(chan struct{}), + domain: m.domain, } go func() { diff --git a/registry/mdns_watcher.go b/registry/mdns_watcher.go index e1c326ff..ce13866f 100644 --- a/registry/mdns_watcher.go +++ b/registry/mdns_watcher.go @@ -11,6 +11,8 @@ type mdnsWatcher struct { wo WatchOptions ch chan *mdns.ServiceEntry exit chan struct{} + // the mdns domain + domain string } func (m *mdnsWatcher) Next() (*Result, error) { @@ -46,13 +48,14 @@ func (m *mdnsWatcher) Next() (*Result, error) { Endpoints: txt.Endpoints, } - // TODO: don't hardcode .local. - if !strings.HasSuffix(e.Name, "."+service.Name+".local.") { + // skip anything without the domain we care about + suffix := fmt.Sprintf(".%s.%s.", service.Name, m.domain) + if !strings.HasSuffix(e.Name, suffix) { continue } service.Nodes = append(service.Nodes, &Node{ - Id: strings.TrimSuffix(e.Name, "."+service.Name+".local."), + Id: strings.TrimSuffix(e.Name, suffix), Address: fmt.Sprintf("%s:%d", e.AddrV4.String(), e.Port), Metadata: txt.Metadata, }) From b076ef906aca82d47489d87109718d6f6cdfacbe Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Mon, 9 Sep 2019 08:57:57 -0700 Subject: [PATCH 86/93] Add service registry --- registry/proto/registry.micro.go | 224 ++++++++ registry/proto/registry.pb.go | 848 +++++++++++++++++++++++++++++++ registry/proto/registry.proto | 73 +++ registry/service/service.go | 155 ++++++ registry/service/util.go | 133 +++++ registry/service/watcher.go | 49 ++ 6 files changed, 1482 insertions(+) create mode 100644 registry/proto/registry.micro.go create mode 100644 registry/proto/registry.pb.go create mode 100644 registry/proto/registry.proto create mode 100644 registry/service/service.go create mode 100644 registry/service/util.go create mode 100644 registry/service/watcher.go diff --git a/registry/proto/registry.micro.go b/registry/proto/registry.micro.go new file mode 100644 index 00000000..3b78fdbf --- /dev/null +++ b/registry/proto/registry.micro.go @@ -0,0 +1,224 @@ +// Code generated by protoc-gen-micro. DO NOT EDIT. +// source: micro/go-micro/registry/proto/registry.proto + +package go_micro_registry + +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + math "math" +) + +import ( + context "context" + client "github.com/micro/go-micro/client" + server "github.com/micro/go-micro/server" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ client.Option +var _ server.Option + +// Client API for Registry service + +type RegistryService interface { + GetService(ctx context.Context, in *GetRequest, opts ...client.CallOption) (*GetResponse, error) + Register(ctx context.Context, in *Service, opts ...client.CallOption) (*EmptyResponse, error) + Deregister(ctx context.Context, in *Service, opts ...client.CallOption) (*EmptyResponse, error) + ListServices(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) + Watch(ctx context.Context, in *WatchRequest, opts ...client.CallOption) (Registry_WatchService, error) +} + +type registryService struct { + c client.Client + name string +} + +func NewRegistryService(name string, c client.Client) RegistryService { + if c == nil { + c = client.NewClient() + } + if len(name) == 0 { + name = "go.micro.registry" + } + return ®istryService{ + c: c, + name: name, + } +} + +func (c *registryService) GetService(ctx context.Context, in *GetRequest, opts ...client.CallOption) (*GetResponse, error) { + req := c.c.NewRequest(c.name, "Registry.GetService", in) + out := new(GetResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *registryService) Register(ctx context.Context, in *Service, opts ...client.CallOption) (*EmptyResponse, error) { + req := c.c.NewRequest(c.name, "Registry.Register", in) + out := new(EmptyResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *registryService) Deregister(ctx context.Context, in *Service, opts ...client.CallOption) (*EmptyResponse, error) { + req := c.c.NewRequest(c.name, "Registry.Deregister", in) + out := new(EmptyResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *registryService) ListServices(ctx context.Context, in *ListRequest, opts ...client.CallOption) (*ListResponse, error) { + req := c.c.NewRequest(c.name, "Registry.ListServices", in) + out := new(ListResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *registryService) Watch(ctx context.Context, in *WatchRequest, opts ...client.CallOption) (Registry_WatchService, error) { + req := c.c.NewRequest(c.name, "Registry.Watch", &WatchRequest{}) + stream, err := c.c.Stream(ctx, req, opts...) + if err != nil { + return nil, err + } + if err := stream.Send(in); err != nil { + return nil, err + } + return ®istryServiceWatch{stream}, nil +} + +type Registry_WatchService interface { + SendMsg(interface{}) error + RecvMsg(interface{}) error + Close() error + Recv() (*Result, error) +} + +type registryServiceWatch struct { + stream client.Stream +} + +func (x *registryServiceWatch) Close() error { + return x.stream.Close() +} + +func (x *registryServiceWatch) SendMsg(m interface{}) error { + return x.stream.Send(m) +} + +func (x *registryServiceWatch) RecvMsg(m interface{}) error { + return x.stream.Recv(m) +} + +func (x *registryServiceWatch) Recv() (*Result, error) { + m := new(Result) + err := x.stream.Recv(m) + if err != nil { + return nil, err + } + return m, nil +} + +// Server API for Registry service + +type RegistryHandler interface { + GetService(context.Context, *GetRequest, *GetResponse) error + Register(context.Context, *Service, *EmptyResponse) error + Deregister(context.Context, *Service, *EmptyResponse) error + ListServices(context.Context, *ListRequest, *ListResponse) error + Watch(context.Context, *WatchRequest, Registry_WatchStream) error +} + +func RegisterRegistryHandler(s server.Server, hdlr RegistryHandler, opts ...server.HandlerOption) error { + type registry interface { + GetService(ctx context.Context, in *GetRequest, out *GetResponse) error + Register(ctx context.Context, in *Service, out *EmptyResponse) error + Deregister(ctx context.Context, in *Service, out *EmptyResponse) error + ListServices(ctx context.Context, in *ListRequest, out *ListResponse) error + Watch(ctx context.Context, stream server.Stream) error + } + type Registry struct { + registry + } + h := ®istryHandler{hdlr} + return s.Handle(s.NewHandler(&Registry{h}, opts...)) +} + +type registryHandler struct { + RegistryHandler +} + +func (h *registryHandler) GetService(ctx context.Context, in *GetRequest, out *GetResponse) error { + return h.RegistryHandler.GetService(ctx, in, out) +} + +func (h *registryHandler) Register(ctx context.Context, in *Service, out *EmptyResponse) error { + return h.RegistryHandler.Register(ctx, in, out) +} + +func (h *registryHandler) Deregister(ctx context.Context, in *Service, out *EmptyResponse) error { + return h.RegistryHandler.Deregister(ctx, in, out) +} + +func (h *registryHandler) ListServices(ctx context.Context, in *ListRequest, out *ListResponse) error { + return h.RegistryHandler.ListServices(ctx, in, out) +} + +func (h *registryHandler) Watch(ctx context.Context, stream server.Stream) error { + m := new(WatchRequest) + if err := stream.Recv(m); err != nil { + return err + } + return h.RegistryHandler.Watch(ctx, m, ®istryWatchStream{stream}) +} + +type Registry_WatchStream interface { + SendMsg(interface{}) error + RecvMsg(interface{}) error + Close() error + Send(*Result) error +} + +type registryWatchStream struct { + stream server.Stream +} + +func (x *registryWatchStream) Close() error { + return x.stream.Close() +} + +func (x *registryWatchStream) SendMsg(m interface{}) error { + return x.stream.Send(m) +} + +func (x *registryWatchStream) RecvMsg(m interface{}) error { + return x.stream.Recv(m) +} + +func (x *registryWatchStream) Send(m *Result) error { + return x.stream.Send(m) +} diff --git a/registry/proto/registry.pb.go b/registry/proto/registry.pb.go new file mode 100644 index 00000000..b05602ed --- /dev/null +++ b/registry/proto/registry.pb.go @@ -0,0 +1,848 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: micro/go-micro/registry/proto/registry.proto + +package go_micro_registry + +import ( + context "context" + fmt "fmt" + proto "github.com/golang/protobuf/proto" + grpc "google.golang.org/grpc" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +// Service represents a go-micro service +type Service struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"` + Metadata map[string]string `protobuf:"bytes,3,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Endpoints []*Endpoint `protobuf:"bytes,4,rep,name=endpoints,proto3" json:"endpoints,omitempty"` + Nodes []*Node `protobuf:"bytes,5,rep,name=nodes,proto3" json:"nodes,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Service) Reset() { *m = Service{} } +func (m *Service) String() string { return proto.CompactTextString(m) } +func (*Service) ProtoMessage() {} +func (*Service) Descriptor() ([]byte, []int) { + return fileDescriptor_f287a6b809166ad2, []int{0} +} + +func (m *Service) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Service.Unmarshal(m, b) +} +func (m *Service) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Service.Marshal(b, m, deterministic) +} +func (m *Service) XXX_Merge(src proto.Message) { + xxx_messageInfo_Service.Merge(m, src) +} +func (m *Service) XXX_Size() int { + return xxx_messageInfo_Service.Size(m) +} +func (m *Service) XXX_DiscardUnknown() { + xxx_messageInfo_Service.DiscardUnknown(m) +} + +var xxx_messageInfo_Service proto.InternalMessageInfo + +func (m *Service) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Service) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + +func (m *Service) GetMetadata() map[string]string { + if m != nil { + return m.Metadata + } + return nil +} + +func (m *Service) GetEndpoints() []*Endpoint { + if m != nil { + return m.Endpoints + } + return nil +} + +func (m *Service) GetNodes() []*Node { + if m != nil { + return m.Nodes + } + return nil +} + +// Node represents the node the service is on +type Node struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + Port int64 `protobuf:"varint,3,opt,name=port,proto3" json:"port,omitempty"` + Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Node) Reset() { *m = Node{} } +func (m *Node) String() string { return proto.CompactTextString(m) } +func (*Node) ProtoMessage() {} +func (*Node) Descriptor() ([]byte, []int) { + return fileDescriptor_f287a6b809166ad2, []int{1} +} + +func (m *Node) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Node.Unmarshal(m, b) +} +func (m *Node) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Node.Marshal(b, m, deterministic) +} +func (m *Node) XXX_Merge(src proto.Message) { + xxx_messageInfo_Node.Merge(m, src) +} +func (m *Node) XXX_Size() int { + return xxx_messageInfo_Node.Size(m) +} +func (m *Node) XXX_DiscardUnknown() { + xxx_messageInfo_Node.DiscardUnknown(m) +} + +var xxx_messageInfo_Node proto.InternalMessageInfo + +func (m *Node) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *Node) GetAddress() string { + if m != nil { + return m.Address + } + return "" +} + +func (m *Node) GetPort() int64 { + if m != nil { + return m.Port + } + return 0 +} + +func (m *Node) GetMetadata() map[string]string { + if m != nil { + return m.Metadata + } + return nil +} + +// Endpoint is a endpoint provided by a service +type Endpoint struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Request *Value `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` + Response *Value `protobuf:"bytes,3,opt,name=response,proto3" json:"response,omitempty"` + Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Endpoint) Reset() { *m = Endpoint{} } +func (m *Endpoint) String() string { return proto.CompactTextString(m) } +func (*Endpoint) ProtoMessage() {} +func (*Endpoint) Descriptor() ([]byte, []int) { + return fileDescriptor_f287a6b809166ad2, []int{2} +} + +func (m *Endpoint) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Endpoint.Unmarshal(m, b) +} +func (m *Endpoint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Endpoint.Marshal(b, m, deterministic) +} +func (m *Endpoint) XXX_Merge(src proto.Message) { + xxx_messageInfo_Endpoint.Merge(m, src) +} +func (m *Endpoint) XXX_Size() int { + return xxx_messageInfo_Endpoint.Size(m) +} +func (m *Endpoint) XXX_DiscardUnknown() { + xxx_messageInfo_Endpoint.DiscardUnknown(m) +} + +var xxx_messageInfo_Endpoint proto.InternalMessageInfo + +func (m *Endpoint) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Endpoint) GetRequest() *Value { + if m != nil { + return m.Request + } + return nil +} + +func (m *Endpoint) GetResponse() *Value { + if m != nil { + return m.Response + } + return nil +} + +func (m *Endpoint) GetMetadata() map[string]string { + if m != nil { + return m.Metadata + } + return nil +} + +// Value is an opaque value for a request or response +type Value struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"` + Values []*Value `protobuf:"bytes,3,rep,name=values,proto3" json:"values,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Value) Reset() { *m = Value{} } +func (m *Value) String() string { return proto.CompactTextString(m) } +func (*Value) ProtoMessage() {} +func (*Value) Descriptor() ([]byte, []int) { + return fileDescriptor_f287a6b809166ad2, []int{3} +} + +func (m *Value) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Value.Unmarshal(m, b) +} +func (m *Value) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Value.Marshal(b, m, deterministic) +} +func (m *Value) XXX_Merge(src proto.Message) { + xxx_messageInfo_Value.Merge(m, src) +} +func (m *Value) XXX_Size() int { + return xxx_messageInfo_Value.Size(m) +} +func (m *Value) XXX_DiscardUnknown() { + xxx_messageInfo_Value.DiscardUnknown(m) +} + +var xxx_messageInfo_Value proto.InternalMessageInfo + +func (m *Value) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *Value) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *Value) GetValues() []*Value { + if m != nil { + return m.Values + } + return nil +} + +// Result is returns by the watcher +type Result struct { + Action string `protobuf:"bytes,1,opt,name=action,proto3" json:"action,omitempty"` + Service *Service `protobuf:"bytes,2,opt,name=service,proto3" json:"service,omitempty"` + Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Result) Reset() { *m = Result{} } +func (m *Result) String() string { return proto.CompactTextString(m) } +func (*Result) ProtoMessage() {} +func (*Result) Descriptor() ([]byte, []int) { + return fileDescriptor_f287a6b809166ad2, []int{4} +} + +func (m *Result) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Result.Unmarshal(m, b) +} +func (m *Result) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Result.Marshal(b, m, deterministic) +} +func (m *Result) XXX_Merge(src proto.Message) { + xxx_messageInfo_Result.Merge(m, src) +} +func (m *Result) XXX_Size() int { + return xxx_messageInfo_Result.Size(m) +} +func (m *Result) XXX_DiscardUnknown() { + xxx_messageInfo_Result.DiscardUnknown(m) +} + +var xxx_messageInfo_Result proto.InternalMessageInfo + +func (m *Result) GetAction() string { + if m != nil { + return m.Action + } + return "" +} + +func (m *Result) GetService() *Service { + if m != nil { + return m.Service + } + return nil +} + +func (m *Result) GetTimestamp() int64 { + if m != nil { + return m.Timestamp + } + return 0 +} + +type EmptyResponse struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *EmptyResponse) Reset() { *m = EmptyResponse{} } +func (m *EmptyResponse) String() string { return proto.CompactTextString(m) } +func (*EmptyResponse) ProtoMessage() {} +func (*EmptyResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f287a6b809166ad2, []int{5} +} + +func (m *EmptyResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_EmptyResponse.Unmarshal(m, b) +} +func (m *EmptyResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_EmptyResponse.Marshal(b, m, deterministic) +} +func (m *EmptyResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_EmptyResponse.Merge(m, src) +} +func (m *EmptyResponse) XXX_Size() int { + return xxx_messageInfo_EmptyResponse.Size(m) +} +func (m *EmptyResponse) XXX_DiscardUnknown() { + xxx_messageInfo_EmptyResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_EmptyResponse proto.InternalMessageInfo + +type GetRequest struct { + Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetRequest) Reset() { *m = GetRequest{} } +func (m *GetRequest) String() string { return proto.CompactTextString(m) } +func (*GetRequest) ProtoMessage() {} +func (*GetRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f287a6b809166ad2, []int{6} +} + +func (m *GetRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetRequest.Unmarshal(m, b) +} +func (m *GetRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetRequest.Marshal(b, m, deterministic) +} +func (m *GetRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetRequest.Merge(m, src) +} +func (m *GetRequest) XXX_Size() int { + return xxx_messageInfo_GetRequest.Size(m) +} +func (m *GetRequest) XXX_DiscardUnknown() { + xxx_messageInfo_GetRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_GetRequest proto.InternalMessageInfo + +func (m *GetRequest) GetService() string { + if m != nil { + return m.Service + } + return "" +} + +type GetResponse struct { + Services []*Service `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *GetResponse) Reset() { *m = GetResponse{} } +func (m *GetResponse) String() string { return proto.CompactTextString(m) } +func (*GetResponse) ProtoMessage() {} +func (*GetResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f287a6b809166ad2, []int{7} +} + +func (m *GetResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_GetResponse.Unmarshal(m, b) +} +func (m *GetResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_GetResponse.Marshal(b, m, deterministic) +} +func (m *GetResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_GetResponse.Merge(m, src) +} +func (m *GetResponse) XXX_Size() int { + return xxx_messageInfo_GetResponse.Size(m) +} +func (m *GetResponse) XXX_DiscardUnknown() { + xxx_messageInfo_GetResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_GetResponse proto.InternalMessageInfo + +func (m *GetResponse) GetServices() []*Service { + if m != nil { + return m.Services + } + return nil +} + +type ListRequest struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListRequest) Reset() { *m = ListRequest{} } +func (m *ListRequest) String() string { return proto.CompactTextString(m) } +func (*ListRequest) ProtoMessage() {} +func (*ListRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f287a6b809166ad2, []int{8} +} + +func (m *ListRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListRequest.Unmarshal(m, b) +} +func (m *ListRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListRequest.Marshal(b, m, deterministic) +} +func (m *ListRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListRequest.Merge(m, src) +} +func (m *ListRequest) XXX_Size() int { + return xxx_messageInfo_ListRequest.Size(m) +} +func (m *ListRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListRequest proto.InternalMessageInfo + +type ListResponse struct { + Services []*Service `protobuf:"bytes,1,rep,name=services,proto3" json:"services,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListResponse) Reset() { *m = ListResponse{} } +func (m *ListResponse) String() string { return proto.CompactTextString(m) } +func (*ListResponse) ProtoMessage() {} +func (*ListResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_f287a6b809166ad2, []int{9} +} + +func (m *ListResponse) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ListResponse.Unmarshal(m, b) +} +func (m *ListResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ListResponse.Marshal(b, m, deterministic) +} +func (m *ListResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListResponse.Merge(m, src) +} +func (m *ListResponse) XXX_Size() int { + return xxx_messageInfo_ListResponse.Size(m) +} +func (m *ListResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListResponse proto.InternalMessageInfo + +func (m *ListResponse) GetServices() []*Service { + if m != nil { + return m.Services + } + return nil +} + +type WatchRequest struct { + // service is optional + Service string `protobuf:"bytes,1,opt,name=service,proto3" json:"service,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *WatchRequest) Reset() { *m = WatchRequest{} } +func (m *WatchRequest) String() string { return proto.CompactTextString(m) } +func (*WatchRequest) ProtoMessage() {} +func (*WatchRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_f287a6b809166ad2, []int{10} +} + +func (m *WatchRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_WatchRequest.Unmarshal(m, b) +} +func (m *WatchRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_WatchRequest.Marshal(b, m, deterministic) +} +func (m *WatchRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_WatchRequest.Merge(m, src) +} +func (m *WatchRequest) XXX_Size() int { + return xxx_messageInfo_WatchRequest.Size(m) +} +func (m *WatchRequest) XXX_DiscardUnknown() { + xxx_messageInfo_WatchRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_WatchRequest proto.InternalMessageInfo + +func (m *WatchRequest) GetService() string { + if m != nil { + return m.Service + } + return "" +} + +func init() { + proto.RegisterType((*Service)(nil), "go.micro.registry.Service") + proto.RegisterMapType((map[string]string)(nil), "go.micro.registry.Service.MetadataEntry") + proto.RegisterType((*Node)(nil), "go.micro.registry.Node") + proto.RegisterMapType((map[string]string)(nil), "go.micro.registry.Node.MetadataEntry") + proto.RegisterType((*Endpoint)(nil), "go.micro.registry.Endpoint") + proto.RegisterMapType((map[string]string)(nil), "go.micro.registry.Endpoint.MetadataEntry") + proto.RegisterType((*Value)(nil), "go.micro.registry.Value") + proto.RegisterType((*Result)(nil), "go.micro.registry.Result") + proto.RegisterType((*EmptyResponse)(nil), "go.micro.registry.EmptyResponse") + proto.RegisterType((*GetRequest)(nil), "go.micro.registry.GetRequest") + proto.RegisterType((*GetResponse)(nil), "go.micro.registry.GetResponse") + proto.RegisterType((*ListRequest)(nil), "go.micro.registry.ListRequest") + proto.RegisterType((*ListResponse)(nil), "go.micro.registry.ListResponse") + proto.RegisterType((*WatchRequest)(nil), "go.micro.registry.WatchRequest") +} + +func init() { + proto.RegisterFile("micro/go-micro/registry/proto/registry.proto", fileDescriptor_f287a6b809166ad2) +} + +var fileDescriptor_f287a6b809166ad2 = []byte{ + // 577 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0x6d, 0x8b, 0xd3, 0x4c, + 0x14, 0x6d, 0x92, 0xbe, 0xde, 0x6e, 0x9f, 0x47, 0x2f, 0xa2, 0x31, 0xbe, 0x95, 0x80, 0x52, 0xc1, + 0xcd, 0x2e, 0x75, 0x11, 0x5f, 0x3e, 0x09, 0x5b, 0x17, 0x64, 0x57, 0x70, 0x04, 0xfd, 0x1c, 0x9b, + 0x4b, 0x0d, 0x6e, 0x5e, 0x9c, 0x99, 0x16, 0xfa, 0x1f, 0x04, 0xff, 0x84, 0x3f, 0xc5, 0x1f, 0x26, + 0x99, 0xcc, 0x34, 0x5d, 0x36, 0xa9, 0x1f, 0x56, 0xbf, 0xcd, 0xcd, 0x9c, 0x73, 0xe6, 0x9e, 0x73, + 0x67, 0x5a, 0x78, 0x92, 0xc4, 0x73, 0x9e, 0x1d, 0x2c, 0xb2, 0xfd, 0x72, 0xc1, 0x69, 0x11, 0x0b, + 0xc9, 0xd7, 0x07, 0x39, 0xcf, 0x64, 0x55, 0x06, 0xaa, 0xc4, 0xeb, 0x8b, 0x2c, 0x50, 0xb8, 0xc0, + 0x6c, 0xf8, 0x3f, 0x6d, 0xe8, 0x7d, 0x20, 0xbe, 0x8a, 0xe7, 0x84, 0x08, 0xed, 0x34, 0x4c, 0xc8, + 0xb5, 0xc6, 0xd6, 0x64, 0xc0, 0xd4, 0x1a, 0x5d, 0xe8, 0xad, 0x88, 0x8b, 0x38, 0x4b, 0x5d, 0x5b, + 0x7d, 0x36, 0x25, 0x1e, 0x43, 0x3f, 0x21, 0x19, 0x46, 0xa1, 0x0c, 0x5d, 0x67, 0xec, 0x4c, 0x86, + 0xd3, 0x49, 0x70, 0x49, 0x3f, 0xd0, 0xda, 0xc1, 0x99, 0x86, 0xce, 0x52, 0xc9, 0xd7, 0x6c, 0xc3, + 0xc4, 0x17, 0x30, 0xa0, 0x34, 0xca, 0xb3, 0x38, 0x95, 0xc2, 0x6d, 0x2b, 0x99, 0x3b, 0x35, 0x32, + 0x33, 0x8d, 0x61, 0x15, 0x1a, 0xf7, 0xa1, 0x93, 0x66, 0x11, 0x09, 0xb7, 0xa3, 0x68, 0xb7, 0x6a, + 0x68, 0xef, 0xb2, 0x88, 0x58, 0x89, 0xf2, 0x5e, 0xc1, 0xe8, 0x42, 0x13, 0x78, 0x0d, 0x9c, 0xaf, + 0xb4, 0xd6, 0x6e, 0x8b, 0x25, 0xde, 0x80, 0xce, 0x2a, 0x3c, 0x5f, 0x92, 0xb6, 0x5a, 0x16, 0x2f, + 0xed, 0xe7, 0x96, 0xff, 0xcb, 0x82, 0x76, 0x21, 0x86, 0xff, 0x81, 0x1d, 0x47, 0x9a, 0x63, 0xc7, + 0x51, 0x91, 0x4f, 0x18, 0x45, 0x9c, 0x84, 0x30, 0xf9, 0xe8, 0xb2, 0x48, 0x33, 0xcf, 0xb8, 0x74, + 0x9d, 0xb1, 0x35, 0x71, 0x98, 0x5a, 0xe3, 0xeb, 0xad, 0xcc, 0x4a, 0xb3, 0x0f, 0x1b, 0xba, 0x6e, + 0x0a, 0xec, 0x6a, 0x36, 0xbe, 0xdb, 0xd0, 0x37, 0x51, 0xd6, 0x8e, 0x7b, 0x0a, 0x3d, 0x4e, 0xdf, + 0x96, 0x24, 0xa4, 0x22, 0x0f, 0xa7, 0x6e, 0x4d, 0x7f, 0x1f, 0x0b, 0x3d, 0x66, 0x80, 0x78, 0x04, + 0x7d, 0x4e, 0x22, 0xcf, 0x52, 0x41, 0xca, 0xec, 0x2e, 0xd2, 0x06, 0x89, 0xb3, 0x4b, 0x51, 0x3c, + 0xde, 0x31, 0xf7, 0x7f, 0x13, 0x47, 0x08, 0x1d, 0xd5, 0x56, 0x6d, 0x14, 0x08, 0x6d, 0xb9, 0xce, + 0x0d, 0x4b, 0xad, 0xf1, 0x10, 0xba, 0x8a, 0x2d, 0xf4, 0x8d, 0x6f, 0x36, 0xaa, 0x71, 0xbe, 0x84, + 0x2e, 0x23, 0xb1, 0x3c, 0x97, 0x78, 0x13, 0xba, 0xe1, 0x5c, 0x16, 0x0f, 0xa9, 0x3c, 0x45, 0x57, + 0x78, 0x04, 0x3d, 0x51, 0x3e, 0x12, 0x1d, 0xb9, 0xd7, 0xfc, 0x8c, 0x98, 0x81, 0xe2, 0x5d, 0x18, + 0xc8, 0x38, 0x21, 0x21, 0xc3, 0x24, 0xd7, 0x57, 0xac, 0xfa, 0xe0, 0xff, 0x0f, 0xa3, 0x59, 0x92, + 0xcb, 0x35, 0xd3, 0x69, 0xfb, 0x8f, 0x00, 0x4e, 0x48, 0x32, 0x3d, 0x31, 0xb7, 0x3a, 0xb2, 0xec, + 0xc5, 0x94, 0xfe, 0x0c, 0x86, 0x0a, 0xa7, 0x87, 0xf4, 0x0c, 0xfa, 0x7a, 0x47, 0xb8, 0x96, 0x72, + 0xbc, 0xab, 0xb9, 0x0d, 0xd6, 0x1f, 0xc1, 0xf0, 0x34, 0x16, 0xe6, 0x3c, 0xff, 0x0d, 0xec, 0x95, + 0xe5, 0x15, 0x65, 0x27, 0xb0, 0xf7, 0x29, 0x94, 0xf3, 0x2f, 0x7f, 0xf4, 0x31, 0xfd, 0xe1, 0x40, + 0x9f, 0x69, 0x21, 0x3c, 0x53, 0xe6, 0xcd, 0xaf, 0xdc, 0xbd, 0x9a, 0xa3, 0xaa, 0x6c, 0xbc, 0xfb, + 0x4d, 0xdb, 0x3a, 0xc9, 0x16, 0xbe, 0x35, 0xd2, 0xc4, 0x71, 0x47, 0xdf, 0xde, 0xb8, 0xee, 0x3e, + 0x5f, 0x98, 0x4a, 0x0b, 0x4f, 0x01, 0x8e, 0x89, 0xff, 0x2d, 0xb5, 0xf7, 0x65, 0xce, 0x9a, 0x22, + 0xb0, 0xce, 0xcb, 0xd6, 0x5c, 0xbc, 0x07, 0x8d, 0xfb, 0x1b, 0xc9, 0x13, 0xe8, 0xa8, 0xc8, 0xb1, + 0x0e, 0xbb, 0x3d, 0x0c, 0xef, 0x76, 0x0d, 0xa0, 0xbc, 0xfa, 0x7e, 0xeb, 0xd0, 0xfa, 0xdc, 0x55, + 0x7f, 0x41, 0x4f, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0x6b, 0x0b, 0x12, 0xd6, 0xb2, 0x06, 0x00, + 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// RegistryClient is the client API for Registry service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type RegistryClient interface { + GetService(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) + Register(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error) + Deregister(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error) + ListServices(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) + Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (Registry_WatchClient, error) +} + +type registryClient struct { + cc *grpc.ClientConn +} + +func NewRegistryClient(cc *grpc.ClientConn) RegistryClient { + return ®istryClient{cc} +} + +func (c *registryClient) GetService(ctx context.Context, in *GetRequest, opts ...grpc.CallOption) (*GetResponse, error) { + out := new(GetResponse) + err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/GetService", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *registryClient) Register(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error) { + out := new(EmptyResponse) + err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/Register", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *registryClient) Deregister(ctx context.Context, in *Service, opts ...grpc.CallOption) (*EmptyResponse, error) { + out := new(EmptyResponse) + err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/Deregister", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *registryClient) ListServices(ctx context.Context, in *ListRequest, opts ...grpc.CallOption) (*ListResponse, error) { + out := new(ListResponse) + err := c.cc.Invoke(ctx, "/go.micro.registry.Registry/ListServices", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *registryClient) Watch(ctx context.Context, in *WatchRequest, opts ...grpc.CallOption) (Registry_WatchClient, error) { + stream, err := c.cc.NewStream(ctx, &_Registry_serviceDesc.Streams[0], "/go.micro.registry.Registry/Watch", opts...) + if err != nil { + return nil, err + } + x := ®istryWatchClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type Registry_WatchClient interface { + Recv() (*Result, error) + grpc.ClientStream +} + +type registryWatchClient struct { + grpc.ClientStream +} + +func (x *registryWatchClient) Recv() (*Result, error) { + m := new(Result) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// RegistryServer is the server API for Registry service. +type RegistryServer interface { + GetService(context.Context, *GetRequest) (*GetResponse, error) + Register(context.Context, *Service) (*EmptyResponse, error) + Deregister(context.Context, *Service) (*EmptyResponse, error) + ListServices(context.Context, *ListRequest) (*ListResponse, error) + Watch(*WatchRequest, Registry_WatchServer) error +} + +func RegisterRegistryServer(s *grpc.Server, srv RegistryServer) { + s.RegisterService(&_Registry_serviceDesc, srv) +} + +func _Registry_GetService_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RegistryServer).GetService(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/go.micro.registry.Registry/GetService", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RegistryServer).GetService(ctx, req.(*GetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Registry_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Service) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RegistryServer).Register(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/go.micro.registry.Registry/Register", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RegistryServer).Register(ctx, req.(*Service)) + } + return interceptor(ctx, in, info, handler) +} + +func _Registry_Deregister_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Service) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RegistryServer).Deregister(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/go.micro.registry.Registry/Deregister", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RegistryServer).Deregister(ctx, req.(*Service)) + } + return interceptor(ctx, in, info, handler) +} + +func _Registry_ListServices_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(RegistryServer).ListServices(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/go.micro.registry.Registry/ListServices", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(RegistryServer).ListServices(ctx, req.(*ListRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Registry_Watch_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(WatchRequest) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(RegistryServer).Watch(m, ®istryWatchServer{stream}) +} + +type Registry_WatchServer interface { + Send(*Result) error + grpc.ServerStream +} + +type registryWatchServer struct { + grpc.ServerStream +} + +func (x *registryWatchServer) Send(m *Result) error { + return x.ServerStream.SendMsg(m) +} + +var _Registry_serviceDesc = grpc.ServiceDesc{ + ServiceName: "go.micro.registry.Registry", + HandlerType: (*RegistryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetService", + Handler: _Registry_GetService_Handler, + }, + { + MethodName: "Register", + Handler: _Registry_Register_Handler, + }, + { + MethodName: "Deregister", + Handler: _Registry_Deregister_Handler, + }, + { + MethodName: "ListServices", + Handler: _Registry_ListServices_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "Watch", + Handler: _Registry_Watch_Handler, + ServerStreams: true, + }, + }, + Metadata: "micro/go-micro/registry/proto/registry.proto", +} diff --git a/registry/proto/registry.proto b/registry/proto/registry.proto new file mode 100644 index 00000000..8d7cc854 --- /dev/null +++ b/registry/proto/registry.proto @@ -0,0 +1,73 @@ +syntax = "proto3"; + +package go.micro.registry; + +service Registry { + rpc GetService(GetRequest) returns (GetResponse) {}; + rpc Register(Service) returns (EmptyResponse) {}; + rpc Deregister(Service) returns (EmptyResponse) {}; + rpc ListServices(ListRequest) returns (ListResponse) {}; + rpc Watch(WatchRequest) returns (stream Result) {}; +} + +// Service represents a go-micro service +message Service { + string name = 1; + string version = 2; + map metadata = 3; + repeated Endpoint endpoints = 4; + repeated Node nodes = 5; +} + +// Node represents the node the service is on +message Node { + string id = 1; + string address = 2; + int64 port = 3; + map metadata = 4; +} + +// Endpoint is a endpoint provided by a service +message Endpoint { + string name = 1; + Value request = 2; + Value response = 3; + map metadata = 4; +} + +// Value is an opaque value for a request or response +message Value { + string name = 1; + string type = 2; + repeated Value values = 3; +} + +// Result is returns by the watcher +message Result { + string action = 1; // create, update, delete + Service service = 2; + int64 timestamp = 3; // unix timestamp +} + +message EmptyResponse {} + +message GetRequest { + string service = 1; +} + +message GetResponse { + repeated Service services = 1; +} + +message ListRequest { + // TODO: filtering +} + +message ListResponse { + repeated Service services = 1; +} + +message WatchRequest { + // service is optional + string service = 1; +} diff --git a/registry/service/service.go b/registry/service/service.go new file mode 100644 index 00000000..796a5283 --- /dev/null +++ b/registry/service/service.go @@ -0,0 +1,155 @@ +// Package service uses the registry service +package service + +import ( + "context" + "time" + + "github.com/micro/go-micro/client" + "github.com/micro/go-micro/registry" + pb "github.com/micro/go-micro/registry/proto" +) + +var ( + // The default service name + DefaultService = "go.micro.service" +) + +type serviceRegistry struct { + opts registry.Options + // name of the registry + name string + // address + address []string + // client to call registry + client pb.RegistryService +} + +func (s *serviceRegistry) callOpts() []client.CallOption { + var opts []client.CallOption + + // set registry address + if len(s.address) > 0 { + opts = append(opts, client.WithAddress(s.address...)) + } + + // set timeout + if s.opts.Timeout > time.Duration(0) { + opts = append(opts, client.WithRequestTimeout(s.opts.Timeout)) + } + + return opts +} + +func (s *serviceRegistry) Init(opts ...registry.Option) error { + for _, o := range opts { + o(&s.opts) + } + return nil +} + +func (s *serviceRegistry) Options() registry.Options { + return s.opts +} + +func (s *serviceRegistry) Register(srv *registry.Service, opts ...registry.RegisterOption) error { + var options registry.RegisterOptions + for _, o := range opts { + o(&options) + } + + // register the service + _, err := s.client.Register(context.TODO(), toProto(srv), s.callOpts()...) + if err != nil { + return err + } + + return nil +} + +func (s *serviceRegistry) Deregister(srv *registry.Service) error { + // deregister the service + _, err := s.client.Deregister(context.TODO(), toProto(srv), s.callOpts()...) + if err != nil { + return err + } + return nil +} + +func (s *serviceRegistry) GetService(name string) ([]*registry.Service, error) { + rsp, err := s.client.GetService(context.TODO(), &pb.GetRequest{ + Service: name, + }, s.callOpts()...) + + if err != nil { + return nil, err + } + + var services []*registry.Service + for _, service := range rsp.Services { + services = append(services, toService(service)) + } + return services, nil +} + +func (s *serviceRegistry) ListServices() ([]*registry.Service, error) { + rsp, err := s.client.ListServices(context.TODO(), &pb.ListRequest{}, s.callOpts()...) + if err != nil { + return nil, err + } + + var services []*registry.Service + for _, service := range rsp.Services { + services = append(services, toService(service)) + } + + return services, nil +} + +func (s *serviceRegistry) Watch(opts ...registry.WatchOption) (registry.Watcher, error) { + var options registry.WatchOptions + for _, o := range opts { + o(&options) + } + + stream, err := s.client.Watch(context.TODO(), &pb.WatchRequest{ + Service: options.Service, + }, s.callOpts()...) + + if err != nil { + return nil, err + } + + return newWatcher(stream), nil +} + +func (s *serviceRegistry) String() string { + return s.name +} + +// NewRegistry returns a new registry service client +func NewRegistry(opts ...registry.Option) registry.Registry { + var options registry.Options + for _, o := range opts { + o(&options) + } + + // use mdns to find the service registry + mReg := registry.NewRegistry() + + // create new client with mdns + cli := client.NewClient( + client.Registry(mReg), + ) + + // service name + // TODO: accept option + name := DefaultService + + return &serviceRegistry{ + opts: options, + name: name, + address: options.Addrs, + client: pb.NewRegistryService(name, cli), + } +} diff --git a/registry/service/util.go b/registry/service/util.go new file mode 100644 index 00000000..3aa7bd40 --- /dev/null +++ b/registry/service/util.go @@ -0,0 +1,133 @@ +package service + +import ( + "github.com/micro/go-micro/registry" + pb "github.com/micro/go-micro/registry/proto" +) + +func values(v []*registry.Value) []*pb.Value { + if len(v) == 0 { + return []*pb.Value{} + } + + var vs []*pb.Value + for _, vi := range v { + vs = append(vs, &pb.Value{ + Name: vi.Name, + Type: vi.Type, + Values: values(vi.Values), + }) + } + return vs +} + +func toValues(v []*pb.Value) []*registry.Value { + if len(v) == 0 { + return []*registry.Value{} + } + + var vs []*registry.Value + for _, vi := range v { + vs = append(vs, ®istry.Value{ + Name: vi.Name, + Type: vi.Type, + Values: toValues(vi.Values), + }) + } + return vs +} + +func toProto(s *registry.Service) *pb.Service { + var endpoints []*pb.Endpoint + for _, ep := range s.Endpoints { + var request, response *pb.Value + + if ep.Request != nil { + request = &pb.Value{ + Name: ep.Request.Name, + Type: ep.Request.Type, + Values: values(ep.Request.Values), + } + } + + if ep.Response != nil { + response = &pb.Value{ + Name: ep.Response.Name, + Type: ep.Response.Type, + Values: values(ep.Response.Values), + } + } + + endpoints = append(endpoints, &pb.Endpoint{ + Name: ep.Name, + Request: request, + Response: response, + Metadata: ep.Metadata, + }) + } + + var nodes []*pb.Node + + for _, node := range s.Nodes { + nodes = append(nodes, &pb.Node{ + Id: node.Id, + Address: node.Address, + Metadata: node.Metadata, + }) + } + + return &pb.Service{ + Name: s.Name, + Version: s.Version, + Metadata: s.Metadata, + Endpoints: endpoints, + Nodes: nodes, + } +} + +func toService(s *pb.Service) *registry.Service { + var endpoints []*registry.Endpoint + for _, ep := range s.Endpoints { + var request, response *registry.Value + + if ep.Request != nil { + request = ®istry.Value{ + Name: ep.Request.Name, + Type: ep.Request.Type, + Values: toValues(ep.Request.Values), + } + } + + if ep.Response != nil { + response = ®istry.Value{ + Name: ep.Response.Name, + Type: ep.Response.Type, + Values: toValues(ep.Response.Values), + } + } + + endpoints = append(endpoints, ®istry.Endpoint{ + Name: ep.Name, + Request: request, + Response: response, + Metadata: ep.Metadata, + }) + } + + var nodes []*registry.Node + for _, node := range s.Nodes { + nodes = append(nodes, ®istry.Node{ + Id: node.Id, + Address: node.Address, + Metadata: node.Metadata, + }) + } + + return ®istry.Service{ + Name: s.Name, + Version: s.Version, + Metadata: s.Metadata, + Endpoints: endpoints, + Nodes: nodes, + } +} diff --git a/registry/service/watcher.go b/registry/service/watcher.go new file mode 100644 index 00000000..c5ede205 --- /dev/null +++ b/registry/service/watcher.go @@ -0,0 +1,49 @@ +package service + +import ( + "github.com/micro/go-micro/registry" + pb "github.com/micro/go-micro/registry/proto" +) + +type serviceWatcher struct { + stream pb.Registry_WatchService + closed chan bool +} + +func (s *serviceWatcher) Next() (*registry.Result, error) { + for { + // check if closed + select { + case <-s.closed: + return nil, registry.ErrWatcherStopped + default: + } + + r, err := s.stream.Recv() + if err != nil { + return nil, err + } + + return ®istry.Result{ + Action: r.Action, + Service: toService(r.Service), + }, nil + } +} + +func (s *serviceWatcher) Stop() { + select { + case <-s.closed: + return + default: + close(s.closed) + s.stream.Close() + } +} + +func newWatcher(stream pb.Registry_WatchService) registry.Watcher { + return &serviceWatcher{ + stream: stream, + closed: make(chan bool), + } +} From 1f44d7a4a1870743561dac1b4fed9a8498efcf74 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Mon, 9 Sep 2019 09:20:17 -0700 Subject: [PATCH 87/93] Add registry handler --- registry/handler/handler.go | 70 +++++++++++++++++++++++++++++++++++++ registry/service/service.go | 8 ++--- registry/service/util.go | 4 +-- registry/service/watcher.go | 2 +- 4 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 registry/handler/handler.go diff --git a/registry/handler/handler.go b/registry/handler/handler.go new file mode 100644 index 00000000..62604e41 --- /dev/null +++ b/registry/handler/handler.go @@ -0,0 +1,70 @@ +package handler + +import ( + "context" + + "github.com/micro/go-micro/errors" + "github.com/micro/go-micro/registry" + pb "github.com/micro/go-micro/registry/proto" + "github.com/micro/go-micro/registry/service" +) + +type Registry struct { + // internal registry + Registry registry.Registry +} + +func (r *Registry) GetService(ctx context.Context, req pb.GetRequest, rsp pb.GetResponse) error { + services, err := r.Registry.GetService(req.Service) + for _, srv := range services { + rsp.Services = append(rsp.Services, service.ToProto(srv)) + } + return nil +} + +func (r *Registry) Register(ctx context.Context, req pb.Service, rsp pb.EmptyResponse) error { + err := r.Registry.Register(service.ToService(req.Service)) + if err != nil { + return errors.InternalServerError("go.micro.registry", err.Error()) + } + return nil +} + +func (r *Registry) Deregister(ctx context.Context, req pb.Service, rsp pb.EmptyResponse) error { + err := r.Registry.Deregister(service.ToService(req.Service)) + if err != nil { + return errors.InternalServerError("go.micro.registry", err.Error()) + } + return nil +} + +func (r *Registry) ListServices(ctx context.Context, req pb.ListRequest, rsp pb.ListResponse) error { + services, err := r.Registry.ListServices() + for _, srv := range services { + rsp.Services = append(rsp.Services, service.ToProto(srv)) + } + return nil +} + +func (r *Registry) Watch(ctx context.Context, req pb.WatchRequest, rsp pb.Registry_WatchStream) error { + watcher, err := r.Registry.Watcher(registry.WatchOption(req.Service)) + if err != nil { + return errors.InternalServerError("go.micro.registry", err.Error()) + } + + for { + next, err := watcher.Next() + if err != nil { + return errors.InternalServerError("go.micro.registry", err.Error()) + } + err = rsp.Send(&pb.Result{ + Action: next.Action, + Service: service.ToProto(next.Service), + }) + if err != nil { + return errors.InternalServerError("go.micro.registry", err.Error()) + } + } + + return nil +} diff --git a/registry/service/service.go b/registry/service/service.go index 796a5283..2473c32f 100644 --- a/registry/service/service.go +++ b/registry/service/service.go @@ -59,7 +59,7 @@ func (s *serviceRegistry) Register(srv *registry.Service, opts ...registry.Regis } // register the service - _, err := s.client.Register(context.TODO(), toProto(srv), s.callOpts()...) + _, err := s.client.Register(context.TODO(), ToProto(srv), s.callOpts()...) if err != nil { return err } @@ -69,7 +69,7 @@ func (s *serviceRegistry) Register(srv *registry.Service, opts ...registry.Regis func (s *serviceRegistry) Deregister(srv *registry.Service) error { // deregister the service - _, err := s.client.Deregister(context.TODO(), toProto(srv), s.callOpts()...) + _, err := s.client.Deregister(context.TODO(), ToProto(srv), s.callOpts()...) if err != nil { return err } @@ -87,7 +87,7 @@ func (s *serviceRegistry) GetService(name string) ([]*registry.Service, error) { var services []*registry.Service for _, service := range rsp.Services { - services = append(services, toService(service)) + services = append(services, ToService(service)) } return services, nil } @@ -100,7 +100,7 @@ func (s *serviceRegistry) ListServices() ([]*registry.Service, error) { var services []*registry.Service for _, service := range rsp.Services { - services = append(services, toService(service)) + services = append(services, ToService(service)) } return services, nil diff --git a/registry/service/util.go b/registry/service/util.go index 3aa7bd40..bbde4095 100644 --- a/registry/service/util.go +++ b/registry/service/util.go @@ -37,7 +37,7 @@ func toValues(v []*pb.Value) []*registry.Value { return vs } -func toProto(s *registry.Service) *pb.Service { +func ToProto(s *registry.Service) *pb.Service { var endpoints []*pb.Endpoint for _, ep := range s.Endpoints { var request, response *pb.Value @@ -85,7 +85,7 @@ func toProto(s *registry.Service) *pb.Service { } } -func toService(s *pb.Service) *registry.Service { +func ToService(s *pb.Service) *registry.Service { var endpoints []*registry.Endpoint for _, ep := range s.Endpoints { var request, response *registry.Value diff --git a/registry/service/watcher.go b/registry/service/watcher.go index c5ede205..08f2144d 100644 --- a/registry/service/watcher.go +++ b/registry/service/watcher.go @@ -26,7 +26,7 @@ func (s *serviceWatcher) Next() (*registry.Result, error) { return ®istry.Result{ Action: r.Action, - Service: toService(r.Service), + Service: ToService(r.Service), }, nil } } From 2c16c7e62f05f04633f859d55d9f305b17114fbe Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Mon, 9 Sep 2019 09:25:47 -0700 Subject: [PATCH 88/93] Fix build breaks --- registry/handler/handler.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/registry/handler/handler.go b/registry/handler/handler.go index 62604e41..f86148cd 100644 --- a/registry/handler/handler.go +++ b/registry/handler/handler.go @@ -14,40 +14,46 @@ type Registry struct { Registry registry.Registry } -func (r *Registry) GetService(ctx context.Context, req pb.GetRequest, rsp pb.GetResponse) error { +func (r *Registry) GetService(ctx context.Context, req *pb.GetRequest, rsp *pb.GetResponse) error { services, err := r.Registry.GetService(req.Service) + if err != nil { + return errors.InternalServerError("go.micro.registry", err.Error()) + } for _, srv := range services { rsp.Services = append(rsp.Services, service.ToProto(srv)) } return nil } -func (r *Registry) Register(ctx context.Context, req pb.Service, rsp pb.EmptyResponse) error { - err := r.Registry.Register(service.ToService(req.Service)) +func (r *Registry) Register(ctx context.Context, req *pb.Service, rsp *pb.EmptyResponse) error { + err := r.Registry.Register(service.ToService(req)) if err != nil { return errors.InternalServerError("go.micro.registry", err.Error()) } return nil } -func (r *Registry) Deregister(ctx context.Context, req pb.Service, rsp pb.EmptyResponse) error { - err := r.Registry.Deregister(service.ToService(req.Service)) +func (r *Registry) Deregister(ctx context.Context, req *pb.Service, rsp *pb.EmptyResponse) error { + err := r.Registry.Deregister(service.ToService(req)) if err != nil { return errors.InternalServerError("go.micro.registry", err.Error()) } return nil } -func (r *Registry) ListServices(ctx context.Context, req pb.ListRequest, rsp pb.ListResponse) error { +func (r *Registry) ListServices(ctx context.Context, req *pb.ListRequest, rsp *pb.ListResponse) error { services, err := r.Registry.ListServices() + if err != nil { + return errors.InternalServerError("go.micro.registry", err.Error()) + } for _, srv := range services { rsp.Services = append(rsp.Services, service.ToProto(srv)) } return nil } -func (r *Registry) Watch(ctx context.Context, req pb.WatchRequest, rsp pb.Registry_WatchStream) error { - watcher, err := r.Registry.Watcher(registry.WatchOption(req.Service)) +func (r *Registry) Watch(ctx context.Context, req *pb.WatchRequest, rsp pb.Registry_WatchStream) error { + watcher, err := r.Registry.Watch(registry.WatchService(req.Service)) if err != nil { return errors.InternalServerError("go.micro.registry", err.Error()) } From b6c6b1327711c5acfa9ae2cf37fba59a8940ebe3 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Mon, 9 Sep 2019 19:09:28 -0700 Subject: [PATCH 89/93] Support plugin loading --- plugin/default.go | 122 +++++++++++++++++++++++++++++++++++++++++++++ plugin/plugin.go | 29 +++++++++++ plugin/template.go | 20 ++++++++ 3 files changed, 171 insertions(+) create mode 100644 plugin/default.go create mode 100644 plugin/plugin.go create mode 100644 plugin/template.go diff --git a/plugin/default.go b/plugin/default.go new file mode 100644 index 00000000..d9d01ec0 --- /dev/null +++ b/plugin/default.go @@ -0,0 +1,122 @@ +// Package plugin provides the ability to load plugins +package plugin + +import ( + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" + pg "plugin" + "strings" + "text/template" + + "github.com/micro/go-micro/broker" + "github.com/micro/go-micro/client" + "github.com/micro/go-micro/client/selector" + "github.com/micro/go-micro/config/cmd" + "github.com/micro/go-micro/registry" + "github.com/micro/go-micro/server" + "github.com/micro/go-micro/transport" +) + +type plugin struct{} + +// Init sets up the plugin +func (p *plugin) Init(c *Config) error { + switch c.Type { + case "broker": + pg, ok := c.NewFunc.(func(...broker.Option) broker.Broker) + if !ok { + return fmt.Errorf("Invalid plugin %s", c.Name) + } + cmd.DefaultBrokers[c.Name] = pg + case "client": + pg, ok := c.NewFunc.(func(...client.Option) client.Client) + if !ok { + return fmt.Errorf("Invalid plugin %s", c.Name) + } + cmd.DefaultClients[c.Name] = pg + case "registry": + pg, ok := c.NewFunc.(func(...registry.Option) registry.Registry) + if !ok { + return fmt.Errorf("Invalid plugin %s", c.Name) + } + cmd.DefaultRegistries[c.Name] = pg + + case "selector": + pg, ok := c.NewFunc.(func(...selector.Option) selector.Selector) + if !ok { + return fmt.Errorf("Invalid plugin %s", c.Name) + } + cmd.DefaultSelectors[c.Name] = pg + case "server": + pg, ok := c.NewFunc.(func(...server.Option) server.Server) + if !ok { + return fmt.Errorf("Invalid plugin %s", c.Name) + } + cmd.DefaultServers[c.Name] = pg + case "transport": + pg, ok := c.NewFunc.(func(...transport.Option) transport.Transport) + if !ok { + return fmt.Errorf("Invalid plugin %s", c.Name) + } + cmd.DefaultTransports[c.Name] = pg + } + return fmt.Errorf("Unknown plugin type: %s for %s", c.Type, c.Name) +} + +// Load loads a plugin created with `go build -buildmode=plugin` +func (p *plugin) Load(path string) (*Config, error) { + plugin, err := pg.Open(path) + if err != nil { + return nil, err + } + s, err := plugin.Lookup("Plugin") + if err != nil { + return nil, err + } + pl, ok := s.(*Config) + if !ok { + return nil, errors.New("could not cast Plugin object") + } + return pl, nil +} + +// Generate creates a go file at the specified path. +// You must use `go build -buildmode=plugin`to build it. +func (p *plugin) Generate(path string, c *Config) error { + f, err := os.Create(path) + if err != nil { + return err + } + defer f.Close() + t, err := template.New(c.Name).Parse(tmpl) + if err != nil { + return err + } + return t.Execute(f, c) +} + +// Build generates a dso plugin using the go command `go build -buildmode=plugin` +func (p *plugin) Build(path string, c *Config) error { + path = strings.TrimSuffix(path, ".so") + + // create go file in tmp path + temp := os.TempDir() + base := filepath.Base(path) + goFile := filepath.Join(temp, base+".go") + + // generate .go file + if err := p.Generate(goFile, c); err != nil { + return err + } + // remove .go file + defer os.Remove(goFile) + + if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil && !os.IsExist(err) { + return fmt.Errorf("Failed to create dir %s: %v", filepath.Dir(path), err) + } + cmd := exec.Command("go", "build", "-buildmode=plugin", "-o", path+".so", goFile) + return cmd.Run() +} diff --git a/plugin/plugin.go b/plugin/plugin.go new file mode 100644 index 00000000..68291a36 --- /dev/null +++ b/plugin/plugin.go @@ -0,0 +1,29 @@ +// Package plugin provides the ability to load plugins +package plugin + +// Plugin is a plugin loaded from a file +type Plugin interface { + // Initialise a plugin with the config + Init(c *Config) error + // Load loads a .so plugin at the given path + Load(path string) (*Config, error) + // Build a .so plugin with config at the path specified + Build(path string, c *Config) error +} + +// Config is the plugin config +type Config struct { + // Name of the plugin e.g rabbitmq + Name string + // Type of the plugin e.g broker + Type string + // Path specifies the import path + Path string + // NewFunc creates an instance of the plugin + NewFunc interface{} +} + +// NewPlugin creates a new plugin interface +func NewPlugin() Plugin { + return &plugin{} +} diff --git a/plugin/template.go b/plugin/template.go new file mode 100644 index 00000000..dd559bfa --- /dev/null +++ b/plugin/template.go @@ -0,0 +1,20 @@ +package plugin + +var ( + tmpl = ` +package main + +import ( + "github.com/micro/go-micro/plugin" + + "{{.Path}}" +) + +var Plugin = plugin.Config{ + Name: "{{.Name}}", + Type: "{{.Type}}", + Path: "{{.Path}}", + NewFunc: {{.Name}}.{{.NewFunc}}, +} +` +) From 3bfbcd5e6aee2676f3fb1f4896858f9aee881f2b Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Mon, 9 Sep 2019 19:43:13 -0700 Subject: [PATCH 90/93] Add default plugin loader --- plugin/plugin.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugin/plugin.go b/plugin/plugin.go index 68291a36..c648bfaf 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -23,6 +23,11 @@ type Config struct { NewFunc interface{} } +var ( + // Default plugin loader + DefaultPlugin = NewPlugin() +) + // NewPlugin creates a new plugin interface func NewPlugin() Plugin { return &plugin{} From a5ce3e32da95c09d981aff47d9669c0a0bd6cce0 Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Mon, 9 Sep 2019 20:17:36 -0700 Subject: [PATCH 91/93] Support plugin loading on service.Init --- config/cmd/cmd.go | 5 +++++ plugin/plugin.go | 12 ++++++++++++ service.go | 21 +++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/config/cmd/cmd.go b/config/cmd/cmd.go index beb4ba97..f80827d2 100644 --- a/config/cmd/cmd.go +++ b/config/cmd/cmd.go @@ -92,6 +92,11 @@ var ( EnvVar: "MICRO_CLIENT_POOL_TTL", Usage: "Sets the client connection pool ttl. e.g 500ms, 5s, 1m. Default: 1m", }, + cli.StringSliceFlag{ + Name: "plugin", + EnvVar: "MICRO_PLUGIN", + Usage: "Comma separated list of plugins e.g /path/to/plugin.so", + }, cli.IntFlag{ Name: "register_ttl", EnvVar: "MICRO_REGISTER_TTL", diff --git a/plugin/plugin.go b/plugin/plugin.go index c648bfaf..c1aecc6e 100644 --- a/plugin/plugin.go +++ b/plugin/plugin.go @@ -32,3 +32,15 @@ var ( func NewPlugin() Plugin { return &plugin{} } + +func Build(path string, c *Config) error { + return DefaultPlugin.Build(path, c) +} + +func Load(path string) (*Config, error) { + return DefaultPlugin.Load(path) +} + +func Init(c *Config) error { + return DefaultPlugin.Init(c) +} diff --git a/service.go b/service.go index e612a4b3..eed7f8ac 100644 --- a/service.go +++ b/service.go @@ -3,6 +3,7 @@ package micro import ( "os" "os/signal" + "strings" "sync" "syscall" @@ -10,7 +11,9 @@ import ( "github.com/micro/go-micro/config/cmd" "github.com/micro/go-micro/debug/handler" "github.com/micro/go-micro/metadata" + "github.com/micro/go-micro/plugin" "github.com/micro/go-micro/server" + "github.com/micro/go-micro/util/log" ) type service struct { @@ -44,6 +47,24 @@ func (s *service) Init(opts ...Option) { } s.once.Do(func() { + // setup the plugins + for _, p := range strings.Split(os.Getenv("MICRO_PLUGIN"), ",") { + if len(p) == 0 { + continue + } + + // load the plugin + c, err := plugin.Load(p) + if err != nil { + log.Fatal(err) + } + + // initialise the plugin + if err := plugin.Init(c); err != nil { + log.Fatal(err) + } + } + // Initialise the command flags, overriding new service _ = s.opts.Cmd.Init( cmd.Broker(&s.opts.Broker), From 065c7d5616b3d5b95ef425fe96e1074ce3f75d4b Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Tue, 10 Sep 2019 05:32:49 -0700 Subject: [PATCH 92/93] fix plugin init --- config/cmd/cmd.go | 5 ----- plugin/default.go | 5 ++++- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/config/cmd/cmd.go b/config/cmd/cmd.go index f80827d2..beb4ba97 100644 --- a/config/cmd/cmd.go +++ b/config/cmd/cmd.go @@ -92,11 +92,6 @@ var ( EnvVar: "MICRO_CLIENT_POOL_TTL", Usage: "Sets the client connection pool ttl. e.g 500ms, 5s, 1m. Default: 1m", }, - cli.StringSliceFlag{ - Name: "plugin", - EnvVar: "MICRO_PLUGIN", - Usage: "Comma separated list of plugins e.g /path/to/plugin.so", - }, cli.IntFlag{ Name: "register_ttl", EnvVar: "MICRO_REGISTER_TTL", diff --git a/plugin/default.go b/plugin/default.go index d9d01ec0..bcafbc3b 100644 --- a/plugin/default.go +++ b/plugin/default.go @@ -62,8 +62,11 @@ func (p *plugin) Init(c *Config) error { return fmt.Errorf("Invalid plugin %s", c.Name) } cmd.DefaultTransports[c.Name] = pg + default: + return fmt.Errorf("Unknown plugin type: %s for %s", c.Type, c.Name) } - return fmt.Errorf("Unknown plugin type: %s for %s", c.Type, c.Name) + + return nil } // Load loads a plugin created with `go build -buildmode=plugin` From b5eea02f7a8ea316636ef93c14af6adba20ffd4a Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Tue, 10 Sep 2019 08:12:28 -0700 Subject: [PATCH 93/93] Move link to tunnel/ --- {network => tunnel}/link/default.go | 0 {network => tunnel}/link/link.go | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {network => tunnel}/link/default.go (100%) rename {network => tunnel}/link/link.go (100%) diff --git a/network/link/default.go b/tunnel/link/default.go similarity index 100% rename from network/link/default.go rename to tunnel/link/default.go diff --git a/network/link/link.go b/tunnel/link/link.go similarity index 100% rename from network/link/link.go rename to tunnel/link/link.go