github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/network/handler/handler.go (about)

     1  package handler
     2  
     3  import (
     4  	"context"
     5  
     6  	pb "github.com/tickoalcantara12/micro/v3/proto/network"
     7  	pbRtr "github.com/tickoalcantara12/micro/v3/proto/router"
     8  	"github.com/tickoalcantara12/micro/v3/service/errors"
     9  	log "github.com/tickoalcantara12/micro/v3/service/logger"
    10  	"github.com/tickoalcantara12/micro/v3/service/network"
    11  	"github.com/tickoalcantara12/micro/v3/service/network/mucp"
    12  	"github.com/tickoalcantara12/micro/v3/service/network/util"
    13  	"github.com/tickoalcantara12/micro/v3/service/router"
    14  	authns "github.com/tickoalcantara12/micro/v3/util/auth/namespace"
    15  	"github.com/tickoalcantara12/micro/v3/util/namespace"
    16  )
    17  
    18  // Network implements network handler
    19  type Network struct {
    20  	Network network.Network
    21  }
    22  
    23  func flatten(n network.Node, visited map[string]bool) []network.Node {
    24  	// if node is nil runaway
    25  	if n == nil {
    26  		return nil
    27  	}
    28  
    29  	// set visisted
    30  	if visited == nil {
    31  		visited = make(map[string]bool)
    32  	}
    33  
    34  	// create new list of nodes
    35  	//nolint:prealloc
    36  	var nodes []network.Node
    37  
    38  	// check if already visited
    39  	if !visited[n.Id()] {
    40  		// append the current node
    41  		nodes = append(nodes, n)
    42  	}
    43  
    44  	// set to visited
    45  	visited[n.Id()] = true
    46  
    47  	// visit the list of peers
    48  	for _, node := range n.Peers() {
    49  		nodes = append(nodes, flatten(node, visited)...)
    50  	}
    51  
    52  	return nodes
    53  }
    54  
    55  func (n *Network) Connect(ctx context.Context, req *pb.ConnectRequest, resp *pb.ConnectResponse) error {
    56  	if len(req.Nodes) == 0 {
    57  		return nil
    58  	}
    59  
    60  	// get list of existing nodes
    61  	nodes := n.Network.Options().Nodes
    62  
    63  	// generate a node map
    64  	nodeMap := make(map[string]bool)
    65  
    66  	for _, node := range nodes {
    67  		nodeMap[node] = true
    68  	}
    69  
    70  	for _, node := range req.Nodes {
    71  		// TODO: we may have been provided a network only
    72  		// so process anad resolve node.Network
    73  		if len(node.Address) == 0 {
    74  			continue
    75  		}
    76  
    77  		// already exists
    78  		if _, ok := nodeMap[node.Address]; ok {
    79  			continue
    80  		}
    81  
    82  		nodeMap[node.Address] = true
    83  		nodes = append(nodes, node.Address)
    84  	}
    85  
    86  	log.Infof("Network.Connect setting peers: %v", nodes)
    87  
    88  	// reinitialise the peers
    89  	n.Network.Init(
    90  		network.Nodes(nodes...),
    91  	)
    92  
    93  	// call the connect method
    94  	n.Network.Connect()
    95  
    96  	return nil
    97  }
    98  
    99  // Nodes returns the list of nodes
   100  func (n *Network) Nodes(ctx context.Context, req *pb.NodesRequest, resp *pb.NodesResponse) error {
   101  	// root node
   102  	nodes := map[string]network.Node{}
   103  
   104  	// get peers encoded into protobuf
   105  	peers := flatten(n.Network, nil)
   106  
   107  	// walk all the peers
   108  	for _, peer := range peers {
   109  		if peer == nil {
   110  			continue
   111  		}
   112  		if _, ok := nodes[peer.Id()]; ok {
   113  			continue
   114  		}
   115  
   116  		// add to visited list
   117  		nodes[n.Network.Id()] = peer
   118  
   119  		resp.Nodes = append(resp.Nodes, &pb.Node{
   120  			Id:      peer.Id(),
   121  			Address: peer.Address(),
   122  		})
   123  	}
   124  
   125  	return nil
   126  }
   127  
   128  // Graph returns the network graph from this root node
   129  func (n *Network) Graph(ctx context.Context, req *pb.GraphRequest, resp *pb.GraphResponse) error {
   130  	depth := uint(req.Depth)
   131  	if depth <= 0 || depth > mucp.MaxDepth {
   132  		depth = mucp.MaxDepth
   133  	}
   134  
   135  	// get peers encoded into protobuf
   136  	peers := util.PeersToProto(n.Network, depth)
   137  
   138  	// set the root node
   139  	resp.Root = peers
   140  
   141  	return nil
   142  }
   143  
   144  // Routes returns a list of routing table routes
   145  func (n *Network) Routes(ctx context.Context, req *pb.RoutesRequest, resp *pb.RoutesResponse) error {
   146  	// default the network to the current users namespace
   147  	if req.Query == nil {
   148  		req.Query = &pb.Query{}
   149  	}
   150  	if len(req.Query.Network) == 0 {
   151  		req.Query.Network = namespace.FromContext(ctx)
   152  	}
   153  
   154  	// authorize the request
   155  	if err := authns.AuthorizeAdmin(ctx, req.Query.Network, "network.Network.Routes"); err != nil {
   156  		return err
   157  	}
   158  
   159  	// build query
   160  	var qOpts []router.LookupOption
   161  	if len(req.Query.Address) > 0 {
   162  		qOpts = append(qOpts, router.LookupAddress(req.Query.Address))
   163  	}
   164  	if len(req.Query.Gateway) > 0 {
   165  		qOpts = append(qOpts, router.LookupGateway(req.Query.Gateway))
   166  	}
   167  	if len(req.Query.Router) > 0 {
   168  		qOpts = append(qOpts, router.LookupRouter(req.Query.Router))
   169  	}
   170  
   171  	// for users in the default namespace, allow access to all namespaces
   172  	if req.Query.Network != namespace.DefaultNamespace {
   173  		qOpts = append(qOpts, router.LookupNetwork(req.Query.Network))
   174  	}
   175  
   176  	var routes []router.Route
   177  	var err error
   178  
   179  	// if a service is specified to a router Lookup
   180  	if len(req.Query.Service) > 0 {
   181  		routes, err = n.Network.Options().Router.Lookup(req.Query.Service, qOpts...)
   182  	} else {
   183  		// otherwise list and filter
   184  		routes, err = n.Network.Options().Router.Table().Read()
   185  		if err == nil {
   186  			// filter the routes
   187  			routes = router.Filter(routes, router.NewLookup(qOpts...))
   188  		}
   189  	}
   190  
   191  	if err != nil {
   192  		return errors.InternalServerError("network.Network.Routes", "failed to list routes: %s", err)
   193  	}
   194  
   195  	for _, route := range routes {
   196  		resp.Routes = append(resp.Routes, &pbRtr.Route{
   197  			Service: route.Service,
   198  			Address: route.Address,
   199  			Gateway: route.Gateway,
   200  			Network: route.Network,
   201  			Router:  route.Router,
   202  			Link:    route.Link,
   203  			Metric:  int64(route.Metric),
   204  		})
   205  	}
   206  
   207  	return nil
   208  }
   209  
   210  // Services returns a list of services based on the routing table
   211  func (n *Network) Services(ctx context.Context, req *pb.ServicesRequest, resp *pb.ServicesResponse) error {
   212  	// authorize the request. only accounts issued by micro (root accounts) can access this endpoint
   213  	if err := authns.AuthorizeAdmin(ctx, authns.DefaultNamespace, "network.Network.Services"); err != nil {
   214  		return err
   215  	}
   216  
   217  	routes, err := n.Network.Options().Router.Table().Read()
   218  	if err != nil {
   219  		return errors.InternalServerError("network.Network.Services", "failed to list services: %s", err)
   220  	}
   221  
   222  	services := make(map[string]bool)
   223  
   224  	for _, route := range routes {
   225  		if route.Service == "*" {
   226  			continue
   227  		}
   228  
   229  		if _, ok := services[route.Service]; ok {
   230  			continue
   231  		}
   232  		services[route.Service] = true
   233  		resp.Services = append(resp.Services, route.Service)
   234  	}
   235  
   236  	return nil
   237  }