move implementations to external repos (#17)

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
2020-08-25 13:44:41 +03:00
committed by GitHub
parent c4a303190a
commit 0f4b1435d9
238 changed files with 151 additions and 37364 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,529 +0,0 @@
package mucp
import (
"container/list"
"errors"
"sync"
"time"
"github.com/unistack-org/micro/v3/network"
pb "github.com/unistack-org/micro/v3/network/mucp/proto"
)
// nodeError tracks node errors
type nodeError struct {
sync.RWMutex
count int
msg error
}
// Increment increments node error count
func (e *nodeError) Update(err error) {
e.Lock()
defer e.Unlock()
e.count++
e.msg = err
}
// Count returns node error count
func (e *nodeError) Count() int {
e.RLock()
defer e.RUnlock()
return e.count
}
func (e *nodeError) Msg() string {
e.RLock()
defer e.RUnlock()
if e.msg != nil {
return e.msg.Error()
}
return ""
}
// status returns node status
type status struct {
sync.RWMutex
err *nodeError
}
// newStatus creates
func newStatus() *status {
return &status{
err: new(nodeError),
}
}
func newPeerStatus(peer *pb.Peer) *status {
status := &status{
err: new(nodeError),
}
// if Node.Status is nil, return empty status
if peer.Node.Status == nil {
return status
}
// if peer.Node.Status.Error is NOT nil, update status fields
if err := peer.Node.Status.GetError(); err != nil {
status.err.count = int(peer.Node.Status.Error.Count)
status.err.msg = errors.New(peer.Node.Status.Error.Msg)
}
return status
}
func (s *status) Error() network.Error {
s.RLock()
defer s.RUnlock()
return &nodeError{
count: s.err.count,
msg: s.err.msg,
}
}
// node is network node
type node struct {
sync.RWMutex
// id is node id
id string
// address is node address
address string
// link on which we communicate with the peer
link string
// peers are nodes with direct link to this node
peers map[string]*node
// network returns the node network
network network.Network
// lastSeen keeps track of node lifetime and updates
lastSeen time.Time
// lastSync keeps track of node last sync request
lastSync time.Time
// err tracks node status
status *status
}
// 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.Network {
return n.network
}
// Status returns node status
func (n *node) Status() network.Status {
n.RLock()
defer n.RUnlock()
return &status{
err: &nodeError{
count: n.status.err.count,
msg: n.status.err.msg,
},
}
}
// walk walks the node graph until some condition is met
func (n *node) walk(until func(peer *node) bool, action func(parent, peer *node)) map[string]*node {
// track the visited nodes
visited := make(map[string]*node)
// queue of the nodes to visit
queue := list.New()
// push node to the back of queue
queue.PushBack(n)
// mark the node as visited
visited[n.id] = n
// keep iterating over the queue until its empty
for queue.Len() > 0 {
// pop the node from the front of the queue
qnode := queue.Front()
if until(qnode.Value.(*node)) {
return visited
}
// iterate through all of the node peers
// mark the visited nodes; enqueue the non-visited
for id, peer := range qnode.Value.(*node).peers {
action(qnode.Value.(*node), peer)
if _, ok := visited[id]; !ok {
visited[id] = peer
queue.PushBack(peer)
}
}
// remove the node from the queue
queue.Remove(qnode)
}
return visited
}
// AddPeer adds a new peer to node topology
// It returns false if the peer already exists
func (n *node) AddPeer(peer *node) error {
n.Lock()
defer n.Unlock()
// get node topology: we need to check if the peer
// we are trying to add is already in our graph
top := n.getTopology(MaxDepth)
untilFoundPeer := func(n *node) bool {
return n.id == peer.id
}
justWalk := func(paent, node *node) {}
visited := top.walk(untilFoundPeer, justWalk)
peerNode, inTop := visited[peer.id]
if _, ok := n.peers[peer.id]; !ok {
if inTop {
// just create a new edge to the existing peer
// but make sure you update the peer link
peerNode.link = peer.link
n.peers[peer.id] = peerNode
return nil
}
n.peers[peer.id] = peer
return nil
}
return ErrPeerExists
}
// DeletePeer deletes a peer from node peers
// It returns true if the peer has been deleted
func (n *node) DeletePeer(id string) bool {
n.Lock()
defer n.Unlock()
delete(n.peers, id)
return true
}
// UpdatePeer updates a peer if it already exists
// It returns error if the peer does not exist
func (n *node) UpdatePeer(peer *node) error {
n.Lock()
defer n.Unlock()
if _, ok := n.peers[peer.id]; ok {
n.peers[peer.id] = peer
return nil
}
return ErrPeerNotFound
}
// RefreshPeer updates node last seen timestamp
// It returns false if the peer has not been found.
func (n *node) RefreshPeer(id, link string, now time.Time) error {
n.Lock()
defer n.Unlock()
peer, ok := n.peers[id]
if !ok {
return ErrPeerNotFound
}
// set peer link
peer.link = link
// set last seen
peer.lastSeen = now
return nil
}
// RefreshSync refreshes nodes sync time
func (n *node) RefreshSync(now time.Time) error {
n.Lock()
defer n.Unlock()
n.lastSync = now
return nil
}
// Nodes returns a slice of all nodes in the whole node topology
func (n *node) Nodes() []network.Node {
// we need to freeze the network graph here
// otherwise we might get inconsistent results
n.RLock()
defer n.RUnlock()
// NOTE: this should never be true
untilNoMorePeers := func(node *node) bool {
return node == nil
}
justWalk := func(parent, node *node) {}
visited := n.walk(untilNoMorePeers, justWalk)
nodes := make([]network.Node, 0, len(visited))
// collect all the nodes and return them
for _, node := range visited {
nodes = append(nodes, node)
}
return nodes
}
// GetPeerNode returns a node from node MaxDepth topology
// It returns nil if the peer was not found
func (n *node) GetPeerNode(id string) *node {
// get node topology up to MaxDepth
top := n.Topology(MaxDepth)
untilFoundPeer := func(n *node) bool {
return n.id == id
}
justWalk := func(paent, node *node) {}
visited := top.walk(untilFoundPeer, justWalk)
peerNode, ok := visited[id]
if !ok {
return nil
}
return peerNode
}
// DeletePeerNode removes peer node from node topology
func (n *node) DeletePeerNode(id string) error {
n.Lock()
defer n.Unlock()
untilNoMorePeers := func(node *node) bool {
return node == nil
}
deleted := make(map[string]*node)
deletePeer := func(parent, node *node) {
if node.id != n.id && node.id == id {
delete(parent.peers, node.id)
deleted[node.id] = node
}
}
n.walk(untilNoMorePeers, deletePeer)
if _, ok := deleted[id]; !ok {
return ErrPeerNotFound
}
return nil
}
// PrunePeer prunes the peers with the given id
func (n *node) PrunePeer(id string) {
n.Lock()
defer n.Unlock()
untilNoMorePeers := func(node *node) bool {
return node == nil
}
prunePeer := func(parent, node *node) {
if node.id != n.id && node.id == id {
delete(parent.peers, node.id)
}
}
n.walk(untilNoMorePeers, prunePeer)
}
// PruneStalePeerNodes prunes the peers that have not been seen for longer than pruneTime
// It returns a map of the the nodes that got pruned
func (n *node) PruneStalePeers(pruneTime time.Duration) map[string]*node {
n.Lock()
defer n.Unlock()
untilNoMorePeers := func(node *node) bool {
return node == nil
}
pruned := make(map[string]*node)
pruneStalePeer := func(parent, node *node) {
if node.id != n.id && time.Since(node.lastSeen) > PruneTime {
delete(parent.peers, node.id)
pruned[node.id] = node
}
}
n.walk(untilNoMorePeers, pruneStalePeer)
return pruned
}
// getTopology traverses node graph and builds node topology
// NOTE: this function is not thread safe
func (n *node) getTopology(depth uint) *node {
// make a copy of yourself
node := &node{
id: n.id,
address: n.address,
peers: make(map[string]*node),
network: n.network,
status: n.status,
lastSeen: n.lastSeen,
}
// return if we reach requested depth or we have no more peers
if depth == 0 || len(n.peers) == 0 {
return node
}
// decrement the depth
depth--
// iterate through our peers and update the node peers
for _, peer := range n.peers {
nodePeer := peer.getTopology(depth)
if _, ok := node.peers[nodePeer.id]; !ok {
node.peers[nodePeer.id] = nodePeer
}
}
return node
}
// Topology returns a copy of the node topology down to given depth
// NOTE: the returned node is a node graph - not a single node
func (n *node) Topology(depth uint) *node {
n.RLock()
defer n.RUnlock()
return n.getTopology(depth)
}
// Peers returns node peers up to MaxDepth
func (n *node) Peers() []network.Node {
n.RLock()
defer n.RUnlock()
peers := make([]network.Node, 0, len(n.peers))
for _, nodePeer := range n.peers {
peer := nodePeer.getTopology(MaxDepth)
peers = append(peers, peer)
}
return peers
}
// UnpackPeerTopology unpacks pb.Peer into node topology of given depth
func UnpackPeerTopology(pbPeer *pb.Peer, lastSeen time.Time, depth uint) *node {
peerNode := &node{
id: pbPeer.Node.Id,
address: pbPeer.Node.Address,
peers: make(map[string]*node),
status: newPeerStatus(pbPeer),
lastSeen: lastSeen,
}
// return if have either reached the depth or have no more peers
if depth == 0 || len(pbPeer.Peers) == 0 {
return peerNode
}
// decrement the depth
depth--
peers := make(map[string]*node)
for _, pbPeer := range pbPeer.Peers {
peer := UnpackPeerTopology(pbPeer, lastSeen, depth)
peers[pbPeer.Node.Id] = peer
}
peerNode.peers = peers
return peerNode
}
func peerProtoTopology(peer network.Node, depth uint) *pb.Peer {
node := &pb.Node{
Id: peer.Id(),
Address: peer.Address(),
Status: &pb.Status{
Error: &pb.Error{
Count: uint32(peer.Status().Error().Count()),
Msg: peer.Status().Error().Msg(),
},
},
}
// set the network name if network is not nil
if peer.Network() != nil {
node.Network = peer.Network().Name()
}
pbPeers := &pb.Peer{
Node: node,
Peers: make([]*pb.Peer, 0),
}
// return if we reached the end of topology or depth
if depth == 0 || len(peer.Peers()) == 0 {
return pbPeers
}
// decrement the depth
depth--
// iterate through peers of peers aka pops
for _, pop := range peer.Peers() {
peer := peerProtoTopology(pop, depth)
pbPeers.Peers = append(pbPeers.Peers, peer)
}
return pbPeers
}
// PeersToProto returns node peers graph encoded into protobuf
func PeersToProto(node network.Node, depth uint) *pb.Peer {
// network node aka root node
pbNode := &pb.Node{
Id: node.Id(),
Address: node.Address(),
Status: &pb.Status{
Error: &pb.Error{
Count: uint32(node.Status().Error().Count()),
Msg: node.Status().Error().Msg(),
},
},
}
// set the network name if network is not nil
if node.Network() != nil {
pbNode.Network = node.Network().Name()
}
// we will build proto topology into this
pbPeers := &pb.Peer{
Node: pbNode,
Peers: make([]*pb.Peer, 0),
}
for _, peer := range node.Peers() {
pbPeer := peerProtoTopology(peer, depth)
pbPeers.Peers = append(pbPeers.Peers, pbPeer)
}
return pbPeers
}

View File

@@ -1,368 +0,0 @@
package mucp
import (
"testing"
"time"
"github.com/unistack-org/micro/v3/network"
pb "github.com/unistack-org/micro/v3/network/mucp/proto"
)
var (
testNodeId = "testNode"
testNodeAddress = "testAddress"
testNodeNetName = "testNetwork"
testNodePeerIds = []string{"peer1", "peer2", "peer3"}
testPeerOfPeerIds = []string{"peer11", "peer12"}
)
func testSetup() *node {
testNode := &node{
id: testNodeId,
address: testNodeAddress,
peers: make(map[string]*node),
network: NewNetwork(network.Name(testNodeNetName)),
status: newStatus(),
}
// add some peers to the node
for _, id := range testNodePeerIds {
testNode.peers[id] = &node{
id: id,
address: testNode.address + "-" + id,
peers: make(map[string]*node),
network: testNode.network,
status: newStatus(),
}
}
// add peers to peer1
// NOTE: these are peers of peers!
for _, id := range testPeerOfPeerIds {
testNode.peers["peer1"].peers[id] = &node{
id: id,
address: testNode.address + "-" + id,
peers: make(map[string]*node),
network: testNode.network,
status: newStatus(),
}
}
// connect peer1 with peer2
testNode.peers["peer1"].peers["peer2"] = testNode.peers["peer2"]
// connect peer2 with peer3
testNode.peers["peer2"].peers["peer3"] = testNode.peers["peer3"]
return testNode
}
func TestNodeId(t *testing.T) {
node := testSetup()
if node.Id() != testNodeId {
t.Errorf("Expected id: %s, found: %s", testNodeId, node.Id())
}
}
func TestNodeAddress(t *testing.T) {
node := testSetup()
if node.Address() != testNodeAddress {
t.Errorf("Expected address: %s, found: %s", testNodeAddress, node.Address())
}
}
func TestNodeNetwork(t *testing.T) {
node := testSetup()
if node.Network().Name() != testNodeNetName {
t.Errorf("Expected network: %s, found: %s", testNodeNetName, node.Network().Name())
}
}
func TestNodes(t *testing.T) {
// single node
single := &node{
id: testNodeId,
address: testNodeAddress,
peers: make(map[string]*node),
network: NewNetwork(network.Name(testNodeNetName)),
}
// get all the nodes including yourself
nodes := single.Nodes()
nodeCount := 1
if len(nodes) != nodeCount {
t.Errorf("Expected to find %d nodes, found: %d", nodeCount, len(nodes))
}
// complicated node graph
node := testSetup()
// get all the nodes including yourself
nodes = node.Nodes()
// compile a list of ids of all nodes in the network into map for easy indexing
nodeIds := make(map[string]bool)
// add yourself
nodeIds[node.id] = true
// add peer Ids
for _, id := range testNodePeerIds {
nodeIds[id] = true
}
// add peer1 peers i.e. peers of peer
for _, id := range testPeerOfPeerIds {
nodeIds[id] = true
}
// we should return the correct number of nodes
if len(nodes) != len(nodeIds) {
t.Errorf("Expected %d nodes, found: %d", len(nodeIds), len(nodes))
}
// iterate through the list of nodes and makes sure all have been returned
for _, node := range nodes {
if _, ok := nodeIds[node.Id()]; !ok {
t.Errorf("Expected to find %s node", node.Id())
}
}
// this is a leaf node
id := "peer11"
if nodePeer := node.GetPeerNode(id); nodePeer == nil {
t.Errorf("Expected to find %s node", id)
}
}
func collectPeerIds(peer network.Node, ids map[string]bool) map[string]bool {
if len(peer.Peers()) == 0 {
return ids
}
// iterate through the whole graph
for _, peer := range peer.Peers() {
ids = collectPeerIds(peer, ids)
if _, ok := ids[peer.Id()]; !ok {
ids[peer.Id()] = true
}
}
return ids
}
func TestPeers(t *testing.T) {
// single node
single := &node{
id: testNodeId,
address: testNodeAddress,
peers: make(map[string]*node),
network: NewNetwork(network.Name(testNodeNetName)),
}
// get node peers
peers := single.Peers()
// there should be no peers
peerCount := 0
if len(peers) != peerCount {
t.Errorf("Expected to find %d nodes, found: %d", peerCount, len(peers))
}
// complicated node graph
node := testSetup()
// list of ids of nodes of MaxDepth
peerIds := make(map[string]bool)
// add peer Ids
for _, id := range testNodePeerIds {
peerIds[id] = true
}
// add peers of peers to peerIds
for _, id := range testPeerOfPeerIds {
peerIds[id] = true
}
// get node peers
peers = node.Peers()
// we will collect all returned Peer Ids into this map
resPeerIds := make(map[string]bool)
for _, peer := range peers {
resPeerIds[peer.Id()] = true
resPeerIds = collectPeerIds(peer, resPeerIds)
}
// if correct, we must collect all peerIds
if len(resPeerIds) != len(peerIds) {
t.Errorf("Expected to find %d peers, found: %d", len(peerIds), len(resPeerIds))
}
for id := range resPeerIds {
if _, ok := peerIds[id]; !ok {
t.Errorf("Expected to find %s peer", id)
}
}
}
func TestDeletePeerNode(t *testing.T) {
// complicated node graph
node := testSetup()
nodeCount := len(node.Nodes())
// should not find non-existent peer node
if err := node.DeletePeerNode("foobar"); err != ErrPeerNotFound {
t.Errorf("Expected: %v, got: %v", ErrPeerNotFound, err)
}
// lets pick one of the peer1 peers
if err := node.DeletePeerNode(testPeerOfPeerIds[0]); err != nil {
t.Errorf("Error deleting peer node: %v", err)
}
nodeDelCount := len(node.Nodes())
if nodeDelCount != nodeCount-1 {
t.Errorf("Expected node count: %d, got: %d", nodeCount-1, nodeDelCount)
}
}
func TestPrunePeer(t *testing.T) {
// complicated node graph
node := testSetup()
before := node.Nodes()
node.PrunePeer("peer3")
now := node.Nodes()
if len(now) != len(before)-1 {
t.Errorf("Expected pruned node count: %d, got: %d", len(before)-1, len(now))
}
}
func TestPruneStalePeers(t *testing.T) {
// complicated node graph
node := testSetup()
nodes := node.Nodes()
// this will delete all nodes besides the root node
pruneTime := 10 * time.Millisecond
time.Sleep(pruneTime)
// should delete all nodes besides (root) node
pruned := node.PruneStalePeers(pruneTime)
if len(pruned) != len(nodes)-1 {
t.Errorf("Expected pruned node count: %d, got: %d", len(nodes)-1, len(pruned))
}
// complicated node graph
node = testSetup()
nodes = node.Nodes()
// set prune time to 100ms and wait for half of it
pruneTime = 100 * time.Millisecond
time.Sleep(pruneTime)
// update the time of peer1
node.peers["peer1"].lastSeen = time.Now()
// should prune all but the root nodes and peer1
pruned = node.PruneStalePeers(pruneTime)
if len(pruned) != len(nodes)-2 {
t.Errorf("Expected pruned node count: %d, got: %d", len(nodes)-2, len(pruned))
}
}
func TestUnpackPeerTopology(t *testing.T) {
pbPeer := &pb.Peer{
Node: &pb.Node{
Id: "newPeer",
Address: "newPeerAddress",
Status: &pb.Status{
Error: &pb.Error{},
},
},
Peers: make([]*pb.Peer, 0),
}
// it should add pbPeer to the single node peers
peer := UnpackPeerTopology(pbPeer, time.Now(), 5)
if peer.id != pbPeer.Node.Id {
t.Errorf("Expected peer id %s, found: %s", pbPeer.Node.Id, peer.id)
}
node := testSetup()
// build a simple topology to update node peer1
peer1 := node.peers["peer1"]
pbPeer1Node := &pb.Node{
Id: peer1.id,
Address: peer1.address,
Status: &pb.Status{
Error: &pb.Error{},
},
}
pbPeer111 := &pb.Peer{
Node: &pb.Node{
Id: "peer111",
Address: "peer111Address",
Status: &pb.Status{
Error: &pb.Error{},
},
},
Peers: make([]*pb.Peer, 0),
}
pbPeer121 := &pb.Peer{
Node: &pb.Node{
Id: "peer121",
Address: "peer121Address",
Status: &pb.Status{
Error: &pb.Error{},
},
},
Peers: make([]*pb.Peer, 0),
}
// topology to update
pbPeer1 := &pb.Peer{
Node: pbPeer1Node,
Peers: []*pb.Peer{pbPeer111, pbPeer121},
}
// unpack peer1 topology
peer = UnpackPeerTopology(pbPeer1, time.Now(), 5)
// make sure peer1 topology has been correctly updated
newPeerIds := []string{pbPeer111.Node.Id, pbPeer121.Node.Id}
for _, id := range newPeerIds {
if _, ok := peer.peers[id]; !ok {
t.Errorf("Expected %s to be a peer of %s", id, "peer1")
}
}
}
func TestPeersToProto(t *testing.T) {
// single node
single := &node{
id: testNodeId,
address: testNodeAddress,
peers: make(map[string]*node),
network: NewNetwork(network.Name(testNodeNetName)),
status: newStatus(),
}
topCount := 0
protoPeers := PeersToProto(single, 0)
if len(protoPeers.Peers) != topCount {
t.Errorf("Expected to find %d nodes, found: %d", topCount, len(protoPeers.Peers))
}
// complicated node graph
node := testSetup()
topCount = 3
// list of ids of nodes of depth 1 i.e. node peers
peerIds := make(map[string]bool)
// add peer Ids
for _, id := range testNodePeerIds {
peerIds[id] = true
}
// depth 1 should give us immmediate neighbours only
protoPeers = PeersToProto(node, 1)
if len(protoPeers.Peers) != topCount {
t.Errorf("Expected to find %d nodes, found: %d", topCount, len(protoPeers.Peers))
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +0,0 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: network/mucp/proto/network.proto
package go_micro_network_mucp
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

View File

@@ -1,115 +0,0 @@
syntax = "proto3";
package go.micro.network.mucp;
// AdvertType defines the type of advert
enum AdvertType {
AdvertAnnounce = 0;
AdvertUpdate = 1;
}
// Advert is router advertsement streamed by Watch
message Advert {
// id of the advertising router
string id = 1;
// type of advertisement
AdvertType type = 2;
// unix timestamp of the advertisement
int64 timestamp = 3;
// TTL of the Advert
int64 ttl = 4;
// events is a list of advertised events
repeated Event events = 5;
}
// EventType defines the type of event
enum EventType {
Create = 0;
Delete = 1;
Update = 2;
}
// Event is routing table event
message Event {
// the unique event id
string id = 1;
// type of event
EventType type = 2;
// unix timestamp of event
int64 timestamp = 3;
// service route
Route route = 4;
}
// Route is a service route
message Route {
// service for the route
string service = 1;
// the address that advertise this route
string address = 2;
// gateway as the next hop
string gateway = 3;
// the network for this destination
string network = 4;
// router if the router id
string router = 5;
// the network link
string link = 6;
// the metric / score of this route
int64 metric = 7;
// metadata for the route
map<string,string> metadata = 8;
}
// Error tracks network errors
message Error {
uint32 count = 1;
string msg = 2;
}
// Status is node status
message Status {
Error error = 1;
}
// Node is network node
message Node {
// node id
string id = 1;
// node address
string address = 2;
// the network
string network = 3;
// associated metadata
map<string,string> metadata = 4;
// node status
Status status = 5;
}
// 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 node
Node node = 1;
}
// Peer is used to advertise node peers
message Peer {
// network node
Node node = 1;
// node peers
repeated Peer peers = 2;
}
// Sync is network sync message
message Sync {
// peer origin
Peer peer = 1;
// node routes
repeated Route routes = 2;
}

View File

@@ -3,11 +3,8 @@ package network
import (
"github.com/google/uuid"
"github.com/unistack-org/micro/v3/proxy"
"github.com/unistack-org/micro/v3/proxy/mucp"
"github.com/unistack-org/micro/v3/router"
regRouter "github.com/unistack-org/micro/v3/router/registry"
"github.com/unistack-org/micro/v3/tunnel"
tmucp "github.com/unistack-org/micro/v3/tunnel/mucp"
)
type Option func(*Options)
@@ -94,8 +91,5 @@ func DefaultOptions() Options {
Id: uuid.New().String(),
Name: "go.micro",
Address: ":0",
Tunnel: tmucp.NewTunnel(),
Router: regRouter.NewRouter(),
Proxy: mucp.NewProxy(),
}
}