storj/pkg/overlay/server.go

227 lines
5.9 KiB
Go
Raw Normal View History

// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
2018-04-12 14:50:22 +01:00
package overlay
import (
"bytes"
2018-04-12 14:50:22 +01:00
"context"
"fmt"
"github.com/gogo/protobuf/proto"
"github.com/zeebo/errs"
Cache (#67) * add reference to dht to overlay client struct * wip * wip * Implement FindNode * get nodes * WIP * Merge in Dennis kademlia code, get it working with our code * ping and moar * WIP trying to get cache working with kademlia * WIP more wiring up * WIP * Update service cli commands * WIP * added GetNodes * added nodes to Kbucket * default transport changed to TCP * GetBuckets interface changed * filling in more routing * timestamp methods * removed store * Added initial network overlay explorer page * Updating and building with dockerfile * Working on adding bootstrap node code * WIP merging in dennis' code * WIP * connects cache to pkg/kademlia implementation * WIP redis cache * testing * Add bootstrap network function for CLI usage * cleanup * call bootstrap on init network * Add BootstrapNetwork function to interface * Merge in dennis kad code * WIP updates to redis/overlay client interface * WIP trying to get the DHT connected to the cache * go mod & test * deps * Bootstrap node now setting up correctly - Need to pass it through CLI commands better * WIP adding refresh and walk functions, added cli flags - added cli flags for custom bootstrap port and ip * PR comments addressed * adding FindStorageNodes to overlay cache * fix GetBucket * using SplitHostPort * Use JoinHostPort * updates to findstoragenodes response and request * WIP merge in progress, having issues with a panic * wip * adjustments * update port for dht bootstrap test * Docker * wip * dockerfile * fixes * makefile changes * Update port in NewKademlia call * Update local kademlia DHT config * kubernetes yaml * cleanup * making tests pass * k8s yaml * lint issues * Edit cli flags to allow for configurable bootstrap IP and Port args * cleanup * cache walking the network now * Rough prototype of Walk function laid out * Move walk function into bootstrap function * Update dht.go * changes to yaml * goimports
2018-06-05 22:06:37 +01:00
"go.uber.org/zap"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
monkit "gopkg.in/spacemonkeygo/monkit.v2"
"storj.io/storj/pkg/pb"
2018-11-30 13:40:13 +00:00
"storj.io/storj/pkg/storj"
"storj.io/storj/storage"
)
// ServerError creates class of errors for stack traces
var ServerError = errs.Class("Server Error")
// Server implements our overlay RPC service
type Server struct {
log *zap.Logger
cache *Cache
metrics *monkit.Registry
nodeStats *pb.NodeStats
Cache (#67) * add reference to dht to overlay client struct * wip * wip * Implement FindNode * get nodes * WIP * Merge in Dennis kademlia code, get it working with our code * ping and moar * WIP trying to get cache working with kademlia * WIP more wiring up * WIP * Update service cli commands * WIP * added GetNodes * added nodes to Kbucket * default transport changed to TCP * GetBuckets interface changed * filling in more routing * timestamp methods * removed store * Added initial network overlay explorer page * Updating and building with dockerfile * Working on adding bootstrap node code * WIP merging in dennis' code * WIP * connects cache to pkg/kademlia implementation * WIP redis cache * testing * Add bootstrap network function for CLI usage * cleanup * call bootstrap on init network * Add BootstrapNetwork function to interface * Merge in dennis kad code * WIP updates to redis/overlay client interface * WIP trying to get the DHT connected to the cache * go mod & test * deps * Bootstrap node now setting up correctly - Need to pass it through CLI commands better * WIP adding refresh and walk functions, added cli flags - added cli flags for custom bootstrap port and ip * PR comments addressed * adding FindStorageNodes to overlay cache * fix GetBucket * using SplitHostPort * Use JoinHostPort * updates to findstoragenodes response and request * WIP merge in progress, having issues with a panic * wip * adjustments * update port for dht bootstrap test * Docker * wip * dockerfile * fixes * makefile changes * Update port in NewKademlia call * Update local kademlia DHT config * kubernetes yaml * cleanup * making tests pass * k8s yaml * lint issues * Edit cli flags to allow for configurable bootstrap IP and Port args * cleanup * cache walking the network now * Rough prototype of Walk function laid out * Move walk function into bootstrap function * Update dht.go * changes to yaml * goimports
2018-06-05 22:06:37 +01:00
}
2018-04-12 14:50:22 +01:00
// NewServer creates a new Overlay Server
func NewServer(log *zap.Logger, cache *Cache, nodeStats *pb.NodeStats) *Server {
return &Server{
cache: cache,
log: log,
metrics: monkit.Default,
nodeStats: nodeStats,
}
}
2018-04-12 14:50:22 +01:00
// Lookup finds the address of a node in our overlay network
func (server *Server) Lookup(ctx context.Context, req *pb.LookupRequest) (*pb.LookupResponse, error) {
na, err := server.cache.Get(ctx, req.NodeId)
Cache (#67) * add reference to dht to overlay client struct * wip * wip * Implement FindNode * get nodes * WIP * Merge in Dennis kademlia code, get it working with our code * ping and moar * WIP trying to get cache working with kademlia * WIP more wiring up * WIP * Update service cli commands * WIP * added GetNodes * added nodes to Kbucket * default transport changed to TCP * GetBuckets interface changed * filling in more routing * timestamp methods * removed store * Added initial network overlay explorer page * Updating and building with dockerfile * Working on adding bootstrap node code * WIP merging in dennis' code * WIP * connects cache to pkg/kademlia implementation * WIP redis cache * testing * Add bootstrap network function for CLI usage * cleanup * call bootstrap on init network * Add BootstrapNetwork function to interface * Merge in dennis kad code * WIP updates to redis/overlay client interface * WIP trying to get the DHT connected to the cache * go mod & test * deps * Bootstrap node now setting up correctly - Need to pass it through CLI commands better * WIP adding refresh and walk functions, added cli flags - added cli flags for custom bootstrap port and ip * PR comments addressed * adding FindStorageNodes to overlay cache * fix GetBucket * using SplitHostPort * Use JoinHostPort * updates to findstoragenodes response and request * WIP merge in progress, having issues with a panic * wip * adjustments * update port for dht bootstrap test * Docker * wip * dockerfile * fixes * makefile changes * Update port in NewKademlia call * Update local kademlia DHT config * kubernetes yaml * cleanup * making tests pass * k8s yaml * lint issues * Edit cli flags to allow for configurable bootstrap IP and Port args * cleanup * cache walking the network now * Rough prototype of Walk function laid out * Move walk function into bootstrap function * Update dht.go * changes to yaml * goimports
2018-06-05 22:06:37 +01:00
if err != nil {
server.log.Error("Error looking up node", zap.Error(err), zap.String("nodeID", req.NodeId.String()))
Cache (#67) * add reference to dht to overlay client struct * wip * wip * Implement FindNode * get nodes * WIP * Merge in Dennis kademlia code, get it working with our code * ping and moar * WIP trying to get cache working with kademlia * WIP more wiring up * WIP * Update service cli commands * WIP * added GetNodes * added nodes to Kbucket * default transport changed to TCP * GetBuckets interface changed * filling in more routing * timestamp methods * removed store * Added initial network overlay explorer page * Updating and building with dockerfile * Working on adding bootstrap node code * WIP merging in dennis' code * WIP * connects cache to pkg/kademlia implementation * WIP redis cache * testing * Add bootstrap network function for CLI usage * cleanup * call bootstrap on init network * Add BootstrapNetwork function to interface * Merge in dennis kad code * WIP updates to redis/overlay client interface * WIP trying to get the DHT connected to the cache * go mod & test * deps * Bootstrap node now setting up correctly - Need to pass it through CLI commands better * WIP adding refresh and walk functions, added cli flags - added cli flags for custom bootstrap port and ip * PR comments addressed * adding FindStorageNodes to overlay cache * fix GetBucket * using SplitHostPort * Use JoinHostPort * updates to findstoragenodes response and request * WIP merge in progress, having issues with a panic * wip * adjustments * update port for dht bootstrap test * Docker * wip * dockerfile * fixes * makefile changes * Update port in NewKademlia call * Update local kademlia DHT config * kubernetes yaml * cleanup * making tests pass * k8s yaml * lint issues * Edit cli flags to allow for configurable bootstrap IP and Port args * cleanup * cache walking the network now * Rough prototype of Walk function laid out * Move walk function into bootstrap function * Update dht.go * changes to yaml * goimports
2018-06-05 22:06:37 +01:00
return nil, err
}
return &pb.LookupResponse{
Node: na,
Cache (#67) * add reference to dht to overlay client struct * wip * wip * Implement FindNode * get nodes * WIP * Merge in Dennis kademlia code, get it working with our code * ping and moar * WIP trying to get cache working with kademlia * WIP more wiring up * WIP * Update service cli commands * WIP * added GetNodes * added nodes to Kbucket * default transport changed to TCP * GetBuckets interface changed * filling in more routing * timestamp methods * removed store * Added initial network overlay explorer page * Updating and building with dockerfile * Working on adding bootstrap node code * WIP merging in dennis' code * WIP * connects cache to pkg/kademlia implementation * WIP redis cache * testing * Add bootstrap network function for CLI usage * cleanup * call bootstrap on init network * Add BootstrapNetwork function to interface * Merge in dennis kad code * WIP updates to redis/overlay client interface * WIP trying to get the DHT connected to the cache * go mod & test * deps * Bootstrap node now setting up correctly - Need to pass it through CLI commands better * WIP adding refresh and walk functions, added cli flags - added cli flags for custom bootstrap port and ip * PR comments addressed * adding FindStorageNodes to overlay cache * fix GetBucket * using SplitHostPort * Use JoinHostPort * updates to findstoragenodes response and request * WIP merge in progress, having issues with a panic * wip * adjustments * update port for dht bootstrap test * Docker * wip * dockerfile * fixes * makefile changes * Update port in NewKademlia call * Update local kademlia DHT config * kubernetes yaml * cleanup * making tests pass * k8s yaml * lint issues * Edit cli flags to allow for configurable bootstrap IP and Port args * cleanup * cache walking the network now * Rough prototype of Walk function laid out * Move walk function into bootstrap function * Update dht.go * changes to yaml * goimports
2018-06-05 22:06:37 +01:00
}, nil
2018-04-12 14:50:22 +01:00
}
// BulkLookup finds the addresses of nodes in our overlay network
func (server *Server) BulkLookup(ctx context.Context, reqs *pb.LookupRequests) (*pb.LookupResponses, error) {
ns, err := server.cache.GetAll(ctx, lookupRequestsToNodeIDs(reqs))
if err != nil {
return nil, ServerError.New("could not get nodes requested %s\n", err)
}
return nodesToLookupResponses(ns), nil
}
2018-04-12 14:50:22 +01:00
// FindStorageNodes searches the overlay network for nodes that meet the provided requirements
func (server *Server) FindStorageNodes(ctx context.Context, req *pb.FindStorageNodesRequest) (resp *pb.FindStorageNodesResponse, err error) {
opts := req.GetOpts()
maxNodes := req.GetMaxNodes()
if maxNodes <= 0 {
maxNodes = opts.GetAmount()
}
excluded := opts.ExcludedNodes
restrictions := opts.GetRestrictions()
reputation := server.nodeStats
var startID storj.NodeID
result := []*pb.Node{}
for {
var nodes []*pb.Node
nodes, startID, err = server.populate(ctx, req.Start, maxNodes, restrictions, reputation, excluded)
if err != nil {
return nil, Error.Wrap(err)
}
resultNodes := []*pb.Node{}
usedAddrs := make(map[string]bool)
for _, n := range nodes {
addr := n.Address.GetAddress()
excluded = append(excluded, n.Id) // exclude all nodes on next iteration
if !usedAddrs[addr] {
resultNodes = append(resultNodes, n)
usedAddrs[addr] = true
}
}
if len(resultNodes) <= 0 {
break
}
result = append(result, resultNodes...)
if len(result) >= int(maxNodes) || startID == (storj.NodeID{}) {
break
}
}
if len(result) < int(maxNodes) {
return nil, status.Errorf(codes.ResourceExhausted, fmt.Sprintf("requested %d nodes, only %d nodes matched the criteria requested", maxNodes, len(result)))
}
if len(result) > int(maxNodes) {
result = result[:maxNodes]
}
return &pb.FindStorageNodesResponse{
Nodes: result,
}, nil
}
func (server *Server) getNodes(ctx context.Context, keys storage.Keys) ([]*pb.Node, error) {
values, err := server.cache.db.GetAll(keys)
Cache (#67) * add reference to dht to overlay client struct * wip * wip * Implement FindNode * get nodes * WIP * Merge in Dennis kademlia code, get it working with our code * ping and moar * WIP trying to get cache working with kademlia * WIP more wiring up * WIP * Update service cli commands * WIP * added GetNodes * added nodes to Kbucket * default transport changed to TCP * GetBuckets interface changed * filling in more routing * timestamp methods * removed store * Added initial network overlay explorer page * Updating and building with dockerfile * Working on adding bootstrap node code * WIP merging in dennis' code * WIP * connects cache to pkg/kademlia implementation * WIP redis cache * testing * Add bootstrap network function for CLI usage * cleanup * call bootstrap on init network * Add BootstrapNetwork function to interface * Merge in dennis kad code * WIP updates to redis/overlay client interface * WIP trying to get the DHT connected to the cache * go mod & test * deps * Bootstrap node now setting up correctly - Need to pass it through CLI commands better * WIP adding refresh and walk functions, added cli flags - added cli flags for custom bootstrap port and ip * PR comments addressed * adding FindStorageNodes to overlay cache * fix GetBucket * using SplitHostPort * Use JoinHostPort * updates to findstoragenodes response and request * WIP merge in progress, having issues with a panic * wip * adjustments * update port for dht bootstrap test * Docker * wip * dockerfile * fixes * makefile changes * Update port in NewKademlia call * Update local kademlia DHT config * kubernetes yaml * cleanup * making tests pass * k8s yaml * lint issues * Edit cli flags to allow for configurable bootstrap IP and Port args * cleanup * cache walking the network now * Rough prototype of Walk function laid out * Move walk function into bootstrap function * Update dht.go * changes to yaml * goimports
2018-06-05 22:06:37 +01:00
if err != nil {
return nil, Error.Wrap(err)
Cache (#67) * add reference to dht to overlay client struct * wip * wip * Implement FindNode * get nodes * WIP * Merge in Dennis kademlia code, get it working with our code * ping and moar * WIP trying to get cache working with kademlia * WIP more wiring up * WIP * Update service cli commands * WIP * added GetNodes * added nodes to Kbucket * default transport changed to TCP * GetBuckets interface changed * filling in more routing * timestamp methods * removed store * Added initial network overlay explorer page * Updating and building with dockerfile * Working on adding bootstrap node code * WIP merging in dennis' code * WIP * connects cache to pkg/kademlia implementation * WIP redis cache * testing * Add bootstrap network function for CLI usage * cleanup * call bootstrap on init network * Add BootstrapNetwork function to interface * Merge in dennis kad code * WIP updates to redis/overlay client interface * WIP trying to get the DHT connected to the cache * go mod & test * deps * Bootstrap node now setting up correctly - Need to pass it through CLI commands better * WIP adding refresh and walk functions, added cli flags - added cli flags for custom bootstrap port and ip * PR comments addressed * adding FindStorageNodes to overlay cache * fix GetBucket * using SplitHostPort * Use JoinHostPort * updates to findstoragenodes response and request * WIP merge in progress, having issues with a panic * wip * adjustments * update port for dht bootstrap test * Docker * wip * dockerfile * fixes * makefile changes * Update port in NewKademlia call * Update local kademlia DHT config * kubernetes yaml * cleanup * making tests pass * k8s yaml * lint issues * Edit cli flags to allow for configurable bootstrap IP and Port args * cleanup * cache walking the network now * Rough prototype of Walk function laid out * Move walk function into bootstrap function * Update dht.go * changes to yaml * goimports
2018-06-05 22:06:37 +01:00
}
nodes := []*pb.Node{}
for _, v := range values {
n := &pb.Node{}
if err := proto.Unmarshal(v, n); err != nil {
return nil, Error.Wrap(err)
}
nodes = append(nodes, n)
}
return nodes, nil
2018-04-12 14:50:22 +01:00
}
func (server *Server) populate(ctx context.Context, startID storj.NodeID, maxNodes int64,
minRestrictions *pb.NodeRestrictions, minReputation *pb.NodeStats,
excluded storj.NodeIDList) ([]*pb.Node, storj.NodeID, error) {
limit := int(maxNodes * 2)
keys, err := server.cache.db.List(startID.Bytes(), limit)
if err != nil {
server.log.Error("Error listing nodes", zap.Error(err))
return nil, storj.NodeID{}, Error.Wrap(err)
}
if len(keys) <= 0 {
server.log.Info("No Keys returned from List operation")
return []*pb.Node{}, startID, nil
}
// TODO: should this be `var result []*pb.Node` ?
result := []*pb.Node{}
nodes, err := server.getNodes(ctx, keys)
if err != nil {
server.log.Error("Error getting nodes", zap.Error(err))
return nil, storj.NodeID{}, Error.Wrap(err)
}
for _, v := range nodes {
if v.Type != pb.NodeType_STORAGE {
continue
}
restrictions := v.GetRestrictions()
reputation := v.GetReputation()
if restrictions.GetFreeBandwidth() < minRestrictions.GetFreeBandwidth() ||
restrictions.GetFreeDisk() < minRestrictions.GetFreeDisk() ||
reputation.GetUptimeRatio() < minReputation.GetUptimeRatio() ||
reputation.GetUptimeCount() < minReputation.GetUptimeCount() ||
reputation.GetAuditSuccessRatio() < minReputation.GetAuditSuccessRatio() ||
reputation.GetAuditCount() < minReputation.GetAuditCount() ||
contains(excluded, v.Id) {
continue
}
result = append(result, v)
}
var nextStart storj.NodeID
if len(keys) < limit {
nextStart = storj.NodeID{}
} else {
nextStart, err = storj.NodeIDFromBytes(keys[len(keys)-1])
}
if err != nil {
return nil, storj.NodeID{}, Error.Wrap(err)
}
return result, nextStart, nil
}
// contains checks if item exists in list
func contains(nodeIDs storj.NodeIDList, searchID storj.NodeID) bool {
for _, id := range nodeIDs {
if bytes.Equal(id.Bytes(), searchID.Bytes()) {
return true
}
}
return false
}
// lookupRequestsToNodeIDs returns the nodeIDs from the LookupRequests
func lookupRequestsToNodeIDs(reqs *pb.LookupRequests) (ids storj.NodeIDList) {
for _, v := range reqs.LookupRequest {
ids = append(ids, v.NodeId)
}
return ids
}
// nodesToLookupResponses returns LookupResponses from the nodes
func nodesToLookupResponses(nodes []*pb.Node) *pb.LookupResponses {
var rs []*pb.LookupResponse
for _, v := range nodes {
r := &pb.LookupResponse{Node: v}
rs = append(rs, r)
}
return &pb.LookupResponses{LookupResponse: rs}
}