2019-01-24 20:15:10 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
2018-05-15 01:31:26 +01:00
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
2018-07-06 20:43:53 +01:00
|
|
|
package pointerdb
|
2018-05-15 01:31:26 +01:00
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
|
2018-08-27 18:28:16 +01:00
|
|
|
"github.com/zeebo/errs"
|
2018-05-15 01:31:26 +01:00
|
|
|
"go.uber.org/zap"
|
2018-05-30 03:47:40 +01:00
|
|
|
"google.golang.org/grpc/codes"
|
|
|
|
"google.golang.org/grpc/status"
|
2018-08-22 16:07:00 +01:00
|
|
|
monkit "gopkg.in/spacemonkeygo/monkit.v2"
|
2018-05-15 01:31:26 +01:00
|
|
|
|
2018-10-09 15:39:14 +01:00
|
|
|
"storj.io/storj/pkg/auth"
|
2019-01-30 20:47:21 +00:00
|
|
|
"storj.io/storj/pkg/identity"
|
2018-10-04 22:00:19 +01:00
|
|
|
"storj.io/storj/pkg/overlay"
|
2018-09-18 05:39:06 +01:00
|
|
|
"storj.io/storj/pkg/pb"
|
2019-02-05 17:22:17 +00:00
|
|
|
_ "storj.io/storj/pkg/pointerdb/auth" // ensures that we add api key flag to current executable
|
|
|
|
"storj.io/storj/pkg/storj"
|
|
|
|
"storj.io/storj/satellite/console"
|
2018-06-13 19:22:32 +01:00
|
|
|
"storj.io/storj/storage"
|
2018-05-15 01:31:26 +01:00
|
|
|
)
|
|
|
|
|
2018-08-22 16:07:00 +01:00
|
|
|
var (
|
2018-08-27 18:28:16 +01:00
|
|
|
mon = monkit.Package()
|
2018-08-27 15:04:46 +01:00
|
|
|
segmentError = errs.Class("segment error")
|
2018-08-22 16:07:00 +01:00
|
|
|
)
|
|
|
|
|
2019-02-05 17:22:17 +00:00
|
|
|
// APIKeys is api keys store methods used by pointerdb
|
|
|
|
type APIKeys interface {
|
|
|
|
GetByKey(ctx context.Context, key console.APIKey) (*console.APIKeyInfo, error)
|
|
|
|
}
|
|
|
|
|
2018-05-15 01:31:26 +01:00
|
|
|
// Server implements the network state RPC service
|
|
|
|
type Server struct {
|
2019-01-19 18:58:53 +00:00
|
|
|
logger *zap.Logger
|
|
|
|
service *Service
|
|
|
|
allocation *AllocationSigner
|
|
|
|
cache *overlay.Cache
|
|
|
|
config Config
|
2019-01-30 20:47:21 +00:00
|
|
|
identity *identity.FullIdentity
|
2019-02-05 17:22:17 +00:00
|
|
|
apiKeys APIKeys
|
2018-05-15 01:31:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewServer creates instance of Server
|
2019-02-05 17:22:17 +00:00
|
|
|
func NewServer(logger *zap.Logger, service *Service, allocation *AllocationSigner, cache *overlay.Cache, config Config, identity *identity.FullIdentity, apiKeys APIKeys) *Server {
|
2018-05-15 01:31:26 +01:00
|
|
|
return &Server{
|
2019-01-19 18:58:53 +00:00
|
|
|
logger: logger,
|
|
|
|
service: service,
|
|
|
|
allocation: allocation,
|
|
|
|
cache: cache,
|
|
|
|
config: config,
|
|
|
|
identity: identity,
|
2019-02-05 17:22:17 +00:00
|
|
|
apiKeys: apiKeys,
|
2018-05-15 01:31:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-18 13:54:08 +00:00
|
|
|
// Close closes resources
|
|
|
|
func (s *Server) Close() error { return nil }
|
|
|
|
|
2019-02-05 17:22:17 +00:00
|
|
|
func (s *Server) validateAuth(ctx context.Context) (*console.APIKeyInfo, error) {
|
|
|
|
APIKey, ok := auth.GetAPIKey(ctx)
|
|
|
|
if !ok {
|
|
|
|
s.logger.Error("unauthorized request: ", zap.Error(status.Errorf(codes.Unauthenticated, "Invalid API credential")))
|
|
|
|
return nil, status.Errorf(codes.Unauthenticated, "Invalid API credential")
|
2019-01-18 13:54:08 +00:00
|
|
|
}
|
|
|
|
|
2019-02-05 17:22:17 +00:00
|
|
|
key, err := console.APIKeyFromBase64(string(APIKey))
|
|
|
|
if err != nil {
|
2018-08-27 18:28:16 +01:00
|
|
|
s.logger.Error("unauthorized request: ", zap.Error(status.Errorf(codes.Unauthenticated, "Invalid API credential")))
|
2019-02-05 17:22:17 +00:00
|
|
|
return nil, status.Errorf(codes.Unauthenticated, "Invalid API credential")
|
2018-06-04 17:45:07 +01:00
|
|
|
}
|
2019-02-05 17:22:17 +00:00
|
|
|
|
|
|
|
keyInfo, err := s.apiKeys.GetByKey(ctx, *key)
|
|
|
|
if err != nil {
|
|
|
|
s.logger.Error("unauthorized request: ", zap.Error(status.Errorf(codes.Unauthenticated, err.Error())))
|
|
|
|
return nil, status.Errorf(codes.Unauthenticated, "Invalid API credential")
|
|
|
|
}
|
|
|
|
|
|
|
|
return keyInfo, nil
|
2018-06-04 17:45:07 +01:00
|
|
|
}
|
|
|
|
|
2018-08-27 15:04:46 +01:00
|
|
|
func (s *Server) validateSegment(req *pb.PutRequest) error {
|
2018-10-08 21:09:28 +01:00
|
|
|
min := s.config.MinRemoteSegmentSize
|
2018-08-27 15:04:46 +01:00
|
|
|
remote := req.GetPointer().Remote
|
2018-11-20 17:09:35 +00:00
|
|
|
remoteSize := req.GetPointer().GetSegmentSize()
|
2018-08-27 15:04:46 +01:00
|
|
|
|
2018-10-08 21:09:28 +01:00
|
|
|
if remote != nil && remoteSize < int64(min) {
|
|
|
|
return segmentError.New("remote segment size %d less than minimum allowed %d", remoteSize, min)
|
2018-08-27 15:04:46 +01:00
|
|
|
}
|
|
|
|
|
2019-01-14 21:19:15 +00:00
|
|
|
max := s.config.MaxInlineSegmentSize.Int()
|
2018-10-08 21:09:28 +01:00
|
|
|
inlineSize := len(req.GetPointer().InlineSegment)
|
|
|
|
|
2018-08-27 15:04:46 +01:00
|
|
|
if inlineSize > max {
|
|
|
|
return segmentError.New("inline segment size %d greater than maximum allowed %d", inlineSize, max)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-02-28 14:14:54 +00:00
|
|
|
func (s *Server) filterValidPieces(pointer *pb.Pointer) error {
|
|
|
|
if pointer.Type == pb.Pointer_REMOTE {
|
|
|
|
var remotePieces []*pb.RemotePiece
|
|
|
|
remote := pointer.Remote
|
|
|
|
for _, piece := range remote.RemotePieces {
|
2019-03-18 10:55:06 +00:00
|
|
|
// TODO enable verification
|
|
|
|
|
|
|
|
// err := auth.VerifyMsg(piece.Hash, piece.NodeId)
|
|
|
|
// if err == nil {
|
|
|
|
// // set to nil after verification to avoid storing in DB
|
|
|
|
// piece.Hash = nil
|
|
|
|
// remotePieces = append(remotePieces, piece)
|
|
|
|
// } else {
|
|
|
|
// // TODO satellite should send Delete request for piece that failed
|
|
|
|
// s.logger.Warn("unable to verify piece hash: %v", zap.Error(err))
|
|
|
|
// }
|
|
|
|
|
|
|
|
remotePieces = append(remotePieces, piece)
|
2019-02-28 14:14:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if int32(len(remotePieces)) < remote.Redundancy.SuccessThreshold {
|
|
|
|
return Error.New("Number of valid pieces is lower then success threshold: %v < %v",
|
|
|
|
len(remotePieces),
|
|
|
|
remote.Redundancy.SuccessThreshold,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
remote.RemotePieces = remotePieces
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-06-29 21:06:25 +01:00
|
|
|
// Put formats and hands off a key/value (path/pointer) to be saved to boltdb
|
2018-07-27 07:02:59 +01:00
|
|
|
func (s *Server) Put(ctx context.Context, req *pb.PutRequest) (resp *pb.PutResponse, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2018-05-15 01:31:26 +01:00
|
|
|
|
2018-08-27 15:04:46 +01:00
|
|
|
err = s.validateSegment(req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, status.Errorf(codes.InvalidArgument, err.Error())
|
|
|
|
}
|
2018-08-27 18:28:16 +01:00
|
|
|
|
2019-02-05 17:22:17 +00:00
|
|
|
keyInfo, err := s.validateAuth(ctx)
|
|
|
|
if err != nil {
|
2018-06-04 17:45:07 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-02-28 14:14:54 +00:00
|
|
|
err = s.filterValidPieces(req.Pointer)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-02-05 17:22:17 +00:00
|
|
|
path := storj.JoinPaths(keyInfo.ProjectID.String(), req.GetPath())
|
|
|
|
if err = s.service.Put(path, req.GetPointer()); err != nil {
|
2018-05-30 03:47:40 +01:00
|
|
|
s.logger.Error("err putting pointer", zap.Error(err))
|
|
|
|
return nil, status.Errorf(codes.Internal, err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
return &pb.PutResponse{}, nil
|
2018-05-15 01:31:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get formats and hands off a file path to get from boltdb
|
2018-07-27 07:02:59 +01:00
|
|
|
func (s *Server) Get(ctx context.Context, req *pb.GetRequest) (resp *pb.GetResponse, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2018-05-15 01:31:26 +01:00
|
|
|
|
2019-02-05 17:22:17 +00:00
|
|
|
keyInfo, err := s.validateAuth(ctx)
|
|
|
|
if err != nil {
|
2018-06-04 17:45:07 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-02-05 17:22:17 +00:00
|
|
|
path := storj.JoinPaths(keyInfo.ProjectID.String(), req.GetPath())
|
|
|
|
pointer, err := s.service.Get(path)
|
2018-05-15 01:31:26 +01:00
|
|
|
if err != nil {
|
2018-08-14 16:22:29 +01:00
|
|
|
if storage.ErrKeyNotFound.Has(err) {
|
|
|
|
return nil, status.Errorf(codes.NotFound, err.Error())
|
|
|
|
}
|
2018-07-27 07:02:59 +01:00
|
|
|
s.logger.Error("err getting pointer", zap.Error(err))
|
2018-05-30 03:47:40 +01:00
|
|
|
return nil, status.Errorf(codes.Internal, err.Error())
|
2018-05-15 01:31:26 +01:00
|
|
|
}
|
|
|
|
|
2019-01-28 19:45:25 +00:00
|
|
|
pba, err := s.PayerBandwidthAllocation(ctx, &pb.PayerBandwidthAllocationRequest{Action: pb.BandwidthAction_GET})
|
2018-10-30 16:24:46 +00:00
|
|
|
if err != nil {
|
|
|
|
s.logger.Error("err getting payer bandwidth allocation", zap.Error(err))
|
|
|
|
return nil, status.Errorf(codes.Internal, err.Error())
|
|
|
|
}
|
|
|
|
|
2018-10-04 22:00:19 +01:00
|
|
|
nodes := []*pb.Node{}
|
|
|
|
|
|
|
|
var r = &pb.GetResponse{
|
2019-02-20 05:36:08 +00:00
|
|
|
Pointer: pointer,
|
|
|
|
Nodes: nil,
|
|
|
|
Pba: pba.GetPba(),
|
2018-10-04 22:00:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if !s.config.Overlay || pointer.Remote == nil {
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, piece := range pointer.Remote.RemotePieces {
|
|
|
|
node, err := s.cache.Get(ctx, piece.NodeId)
|
|
|
|
if err != nil {
|
2019-02-01 17:55:47 +00:00
|
|
|
s.logger.Error("Error getting node from cache", zap.String("ID", piece.NodeId.String()), zap.Error(err))
|
2019-02-05 21:12:27 +00:00
|
|
|
continue
|
2018-10-04 22:00:19 +01:00
|
|
|
}
|
|
|
|
nodes = append(nodes, node)
|
|
|
|
}
|
|
|
|
|
2019-01-02 18:47:34 +00:00
|
|
|
for _, v := range nodes {
|
2019-02-01 17:55:47 +00:00
|
|
|
if v != nil {
|
|
|
|
v.Type.DPanicOnInvalid("pdb server Get")
|
|
|
|
}
|
2019-01-02 18:47:34 +00:00
|
|
|
}
|
2018-10-04 22:00:19 +01:00
|
|
|
r = &pb.GetResponse{
|
2019-02-20 05:36:08 +00:00
|
|
|
Pointer: pointer,
|
|
|
|
Nodes: nodes,
|
|
|
|
Pba: pba.GetPba(),
|
2018-10-04 22:00:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return r, nil
|
2018-05-15 01:31:26 +01:00
|
|
|
}
|
|
|
|
|
2018-09-07 15:20:15 +01:00
|
|
|
// List returns all Path keys in the Pointers bucket
|
2018-07-27 07:02:59 +01:00
|
|
|
func (s *Server) List(ctx context.Context, req *pb.ListRequest) (resp *pb.ListResponse, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2018-05-15 01:31:26 +01:00
|
|
|
|
2019-02-05 17:22:17 +00:00
|
|
|
keyInfo, err := s.validateAuth(ctx)
|
|
|
|
if err != nil {
|
2018-06-04 17:45:07 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-02-05 17:22:17 +00:00
|
|
|
prefix := storj.JoinPaths(keyInfo.ProjectID.String(), req.Prefix)
|
|
|
|
items, more, err := s.service.List(prefix, req.StartAfter, req.EndBefore, req.Recursive, req.Limit, req.MetaFlags)
|
2018-07-27 07:02:59 +01:00
|
|
|
if err != nil {
|
2018-09-07 15:20:15 +01:00
|
|
|
return nil, status.Errorf(codes.Internal, "ListV2: %v", err)
|
2018-07-27 07:02:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return &pb.ListResponse{Items: items, More: more}, nil
|
|
|
|
}
|
|
|
|
|
2018-05-15 01:31:26 +01:00
|
|
|
// Delete formats and hands off a file path to delete from boltdb
|
2018-07-27 07:02:59 +01:00
|
|
|
func (s *Server) Delete(ctx context.Context, req *pb.DeleteRequest) (resp *pb.DeleteResponse, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2018-05-15 01:31:26 +01:00
|
|
|
|
2019-02-05 17:22:17 +00:00
|
|
|
keyInfo, err := s.validateAuth(ctx)
|
|
|
|
if err != nil {
|
2018-06-04 17:45:07 +01:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-02-05 17:22:17 +00:00
|
|
|
path := storj.JoinPaths(keyInfo.ProjectID.String(), req.GetPath())
|
|
|
|
err = s.service.Delete(path)
|
2018-05-15 01:31:26 +01:00
|
|
|
if err != nil {
|
2018-06-29 21:06:25 +01:00
|
|
|
s.logger.Error("err deleting path and pointer", zap.Error(err))
|
2018-05-30 03:47:40 +01:00
|
|
|
return nil, status.Errorf(codes.Internal, err.Error())
|
2018-05-15 01:31:26 +01:00
|
|
|
}
|
2018-11-29 20:59:26 +00:00
|
|
|
|
2018-05-30 03:47:40 +01:00
|
|
|
return &pb.DeleteResponse{}, nil
|
2018-08-13 09:39:45 +01:00
|
|
|
}
|
2018-10-09 17:09:33 +01:00
|
|
|
|
|
|
|
// Iterate iterates over items based on IterateRequest
|
2019-01-10 12:07:08 +00:00
|
|
|
func (s *Server) Iterate(ctx context.Context, req *pb.IterateRequest, f func(it storage.Iterator) error) (err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
2019-02-05 17:22:17 +00:00
|
|
|
keyInfo, err := s.validateAuth(ctx)
|
|
|
|
if err != nil {
|
2019-01-10 12:07:08 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-02-05 17:22:17 +00:00
|
|
|
prefix := storj.JoinPaths(keyInfo.ProjectID.String(), req.Prefix)
|
|
|
|
return s.service.Iterate(prefix, req.First, req.Recurse, req.Reverse, f)
|
2018-10-11 15:35:55 +01:00
|
|
|
}
|
2018-10-30 16:24:46 +00:00
|
|
|
|
2019-02-22 21:17:35 +00:00
|
|
|
// PayerBandwidthAllocation returns OrderLimit struct, signed and with given action type
|
2019-01-19 18:58:53 +00:00
|
|
|
func (s *Server) PayerBandwidthAllocation(ctx context.Context, req *pb.PayerBandwidthAllocationRequest) (res *pb.PayerBandwidthAllocationResponse, err error) {
|
2019-01-10 12:07:08 +00:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
2019-02-05 17:22:17 +00:00
|
|
|
_, err = s.validateAuth(ctx)
|
|
|
|
if err != nil {
|
2019-01-10 12:07:08 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-10-30 16:24:46 +00:00
|
|
|
// TODO(michal) should be replaced with renter id when available
|
2019-01-04 16:26:26 +00:00
|
|
|
// retrieve the public key
|
2019-01-30 20:47:21 +00:00
|
|
|
pi, err := identity.PeerIdentityFromContext(ctx)
|
2019-01-04 16:26:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-01-19 18:58:53 +00:00
|
|
|
pba, err := s.allocation.PayerBandwidthAllocation(ctx, pi, req.GetAction())
|
2019-01-04 16:26:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, status.Errorf(codes.Internal, err.Error())
|
|
|
|
}
|
|
|
|
|
2019-01-19 18:58:53 +00:00
|
|
|
return &pb.PayerBandwidthAllocationResponse{Pba: pba}, nil
|
2018-10-30 16:24:46 +00:00
|
|
|
}
|