2018-08-07 18:01:02 +00:00
|
|
|
package adder
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/ipfs/ipfs-cluster/api"
|
|
|
|
"github.com/ipfs/ipfs-cluster/rpcutil"
|
|
|
|
|
|
|
|
cid "github.com/ipfs/go-cid"
|
|
|
|
ipld "github.com/ipfs/go-ipld-format"
|
2019-06-14 10:41:11 +00:00
|
|
|
peer "github.com/libp2p/go-libp2p-core/peer"
|
2018-10-17 13:28:03 +00:00
|
|
|
rpc "github.com/libp2p/go-libp2p-gorpc"
|
2018-08-07 18:01:02 +00:00
|
|
|
)
|
|
|
|
|
2019-08-13 14:02:43 +00:00
|
|
|
// ErrBlockAdder is returned when adding a to multiple destinations
|
|
|
|
// block fails on all of them.
|
|
|
|
var ErrBlockAdder = errors.New("failed to put block on all destinations")
|
|
|
|
|
2019-08-05 05:01:07 +00:00
|
|
|
// BlockAdder implements "github.com/ipfs/go-ipld-format".NodeAdder.
|
2019-08-13 14:02:43 +00:00
|
|
|
// It helps sending nodes to multiple destinations, as long as one of
|
|
|
|
// them is still working.
|
2019-08-05 05:01:07 +00:00
|
|
|
type BlockAdder struct {
|
|
|
|
dests []peer.ID
|
|
|
|
rpcClient *rpc.Client
|
|
|
|
}
|
|
|
|
|
2019-08-13 14:02:43 +00:00
|
|
|
// NewBlockAdder creates a BlockAdder given an rpc client and allocated peers.
|
2019-08-05 05:01:07 +00:00
|
|
|
func NewBlockAdder(rpcClient *rpc.Client, dests []peer.ID) *BlockAdder {
|
|
|
|
return &BlockAdder{
|
|
|
|
dests: dests,
|
|
|
|
rpcClient: rpcClient,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-13 14:02:43 +00:00
|
|
|
// Add puts an ipld node to the allocated destinations.
|
2019-08-05 05:01:07 +00:00
|
|
|
func (ba *BlockAdder) Add(ctx context.Context, node ipld.Node) error {
|
2019-08-13 14:02:43 +00:00
|
|
|
nodeSerial := ipldNodeToNodeWithMeta(node)
|
2019-08-05 05:01:07 +00:00
|
|
|
|
|
|
|
ctxs, cancels := rpcutil.CtxsWithCancel(ctx, len(ba.dests))
|
2018-08-07 18:01:02 +00:00
|
|
|
defer rpcutil.MultiCancel(cancels)
|
|
|
|
|
2019-08-05 05:01:07 +00:00
|
|
|
logger.Debugf("block put %s to %s", nodeSerial.Cid, ba.dests)
|
|
|
|
errs := ba.rpcClient.MultiCall(
|
2018-08-07 18:01:02 +00:00
|
|
|
ctxs,
|
2019-08-05 05:01:07 +00:00
|
|
|
ba.dests,
|
2019-05-04 20:36:10 +00:00
|
|
|
"IPFSConnector",
|
|
|
|
"BlockPut",
|
2019-08-05 05:01:07 +00:00
|
|
|
nodeSerial,
|
|
|
|
rpcutil.RPCDiscardReplies(len(ba.dests)),
|
2018-08-07 18:01:02 +00:00
|
|
|
)
|
2019-08-05 05:01:07 +00:00
|
|
|
|
2020-02-03 09:30:04 +00:00
|
|
|
var successfulDests []peer.ID
|
2020-05-17 00:50:54 +00:00
|
|
|
numErrs := 0
|
2019-08-05 05:01:07 +00:00
|
|
|
for i, e := range errs {
|
2019-08-13 14:02:43 +00:00
|
|
|
if e != nil {
|
|
|
|
logger.Errorf("BlockPut on %s: %s", ba.dests[i], e)
|
2020-05-17 00:50:54 +00:00
|
|
|
numErrs++
|
2019-08-13 14:02:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// RPCErrors include server errors (wrong RPC methods), client
|
|
|
|
// errors (creating, writing or reading streams) and
|
|
|
|
// authorization errors, but not IPFS errors from a failed blockput
|
|
|
|
// for example.
|
|
|
|
if rpc.IsRPCError(e) {
|
2019-08-05 05:01:07 +00:00
|
|
|
continue
|
|
|
|
}
|
2020-02-03 09:30:04 +00:00
|
|
|
successfulDests = append(successfulDests, ba.dests[i])
|
2019-08-05 05:01:07 +00:00
|
|
|
}
|
|
|
|
|
2020-05-17 00:50:54 +00:00
|
|
|
// If all requests resulted in errors, fail.
|
|
|
|
// Successful dests will have members when no errors happened
|
|
|
|
// or when an error happened but it was not an RPC error.
|
|
|
|
// As long as BlockPut worked in 1 destination, we move on.
|
|
|
|
if numErrs == len(ba.dests) || len(successfulDests) == 0 {
|
2019-08-13 14:02:43 +00:00
|
|
|
return ErrBlockAdder
|
2019-08-05 05:01:07 +00:00
|
|
|
}
|
|
|
|
|
2020-02-03 09:30:04 +00:00
|
|
|
ba.dests = successfulDests
|
2019-08-05 05:01:07 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddMany puts multiple ipld nodes to allocated destinations.
|
|
|
|
func (ba *BlockAdder) AddMany(ctx context.Context, nodes []ipld.Node) error {
|
|
|
|
for _, node := range nodes {
|
|
|
|
err := ba.Add(ctx, node)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2018-08-07 18:01:02 +00:00
|
|
|
}
|
|
|
|
|
2019-08-13 14:02:43 +00:00
|
|
|
// ipldNodeToNodeSerial converts an ipld.Node to NodeWithMeta.
|
|
|
|
func ipldNodeToNodeWithMeta(n ipld.Node) *api.NodeWithMeta {
|
|
|
|
size, err := n.Size()
|
|
|
|
if err != nil {
|
2020-03-13 20:40:02 +00:00
|
|
|
logger.Warn(err)
|
2019-08-13 14:02:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return &api.NodeWithMeta{
|
|
|
|
Cid: n.Cid(),
|
|
|
|
Data: n.RawData(),
|
|
|
|
CumSize: size,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-08 19:10:42 +00:00
|
|
|
// BlockAllocate helps allocating blocks to peers.
|
|
|
|
func BlockAllocate(ctx context.Context, rpc *rpc.Client, pinOpts api.PinOptions) ([]peer.ID, error) {
|
|
|
|
// Find where to allocate this file
|
2019-02-27 17:04:35 +00:00
|
|
|
var allocsStr []peer.ID
|
2018-08-08 19:10:42 +00:00
|
|
|
err := rpc.CallContext(
|
|
|
|
ctx,
|
|
|
|
"",
|
|
|
|
"Cluster",
|
|
|
|
"BlockAllocate",
|
2019-02-27 17:04:35 +00:00
|
|
|
api.PinWithOpts(cid.Undef, pinOpts),
|
2018-08-08 19:10:42 +00:00
|
|
|
&allocsStr,
|
|
|
|
)
|
2019-02-27 17:04:35 +00:00
|
|
|
return allocsStr, err
|
2018-08-08 19:10:42 +00:00
|
|
|
}
|
|
|
|
|
2018-08-08 19:29:21 +00:00
|
|
|
// Pin helps sending local RPC pin requests.
|
2019-02-27 17:04:35 +00:00
|
|
|
func Pin(ctx context.Context, rpc *rpc.Client, pin *api.Pin) error {
|
2018-08-08 19:10:42 +00:00
|
|
|
if pin.ReplicationFactorMin < 0 {
|
|
|
|
pin.Allocations = []peer.ID{}
|
|
|
|
}
|
|
|
|
logger.Debugf("adder pinning %+v", pin)
|
Improve pin/unpin method signatures (#843)
* Improve pin/unpin method signatures:
These changes the following Cluster Go API methods:
* -> Cluster.Pin(ctx, cid, options) (pin, error)
* -> Cluster.Unpin(ctx, cid) (pin, error)
* -> Cluster.PinPath(ctx, path, opts) (pin,error)
Pin and Unpin now return the pinned object.
The signature of the methods now matches that of the API Client, is clearer as
to what options the user can set and is aligned with PinPath, UnpinPath, which
returned pin methods.
The REST API now returns the Pinned/Unpinned object rather than 204-Accepted.
This was necessary for a cleaner pin/update approach, which I'm working on in
another branch.
Most of the changes here are updating tests to the new signatures
* Adapt load-balancing client to new Pin/Unpin signatures
* cluster.go: Fix typo
Co-Authored-By: Kishan Sagathiya <kishansagathiya@gmail.com>
* cluster.go: Fix typo
Co-Authored-By: Kishan Sagathiya <kishansagathiya@gmail.com>
2019-07-22 13:39:11 +00:00
|
|
|
var pinResp api.Pin
|
2018-08-08 19:10:42 +00:00
|
|
|
return rpc.CallContext(
|
|
|
|
ctx,
|
|
|
|
"", // use ourself to pin
|
|
|
|
"Cluster",
|
|
|
|
"Pin",
|
2019-02-27 17:04:35 +00:00
|
|
|
pin,
|
Improve pin/unpin method signatures (#843)
* Improve pin/unpin method signatures:
These changes the following Cluster Go API methods:
* -> Cluster.Pin(ctx, cid, options) (pin, error)
* -> Cluster.Unpin(ctx, cid) (pin, error)
* -> Cluster.PinPath(ctx, path, opts) (pin,error)
Pin and Unpin now return the pinned object.
The signature of the methods now matches that of the API Client, is clearer as
to what options the user can set and is aligned with PinPath, UnpinPath, which
returned pin methods.
The REST API now returns the Pinned/Unpinned object rather than 204-Accepted.
This was necessary for a cleaner pin/update approach, which I'm working on in
another branch.
Most of the changes here are updating tests to the new signatures
* Adapt load-balancing client to new Pin/Unpin signatures
* cluster.go: Fix typo
Co-Authored-By: Kishan Sagathiya <kishansagathiya@gmail.com>
* cluster.go: Fix typo
Co-Authored-By: Kishan Sagathiya <kishansagathiya@gmail.com>
2019-07-22 13:39:11 +00:00
|
|
|
&pinResp,
|
2018-08-08 19:10:42 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2018-08-07 18:01:02 +00:00
|
|
|
// ErrDAGNotFound is returned whenever we try to get a block from the DAGService.
|
|
|
|
var ErrDAGNotFound = errors.New("dagservice: block not found")
|
|
|
|
|
|
|
|
// BaseDAGService partially implements an ipld.DAGService.
|
|
|
|
// It provides the methods which are not needed by ClusterDAGServices
|
|
|
|
// (Get*, Remove*) so that they can save adding this code.
|
|
|
|
type BaseDAGService struct {
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get always returns errNotFound
|
2018-09-22 01:00:10 +00:00
|
|
|
func (dag BaseDAGService) Get(ctx context.Context, key cid.Cid) (ipld.Node, error) {
|
2018-08-07 18:01:02 +00:00
|
|
|
return nil, ErrDAGNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetMany returns an output channel that always emits an error
|
2018-09-22 01:00:10 +00:00
|
|
|
func (dag BaseDAGService) GetMany(ctx context.Context, keys []cid.Cid) <-chan *ipld.NodeOption {
|
2018-08-07 18:01:02 +00:00
|
|
|
out := make(chan *ipld.NodeOption, 1)
|
|
|
|
out <- &ipld.NodeOption{Err: fmt.Errorf("failed to fetch all nodes")}
|
|
|
|
close(out)
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove is a nop
|
2018-09-22 01:00:10 +00:00
|
|
|
func (dag BaseDAGService) Remove(ctx context.Context, key cid.Cid) error {
|
2018-08-07 18:01:02 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemoveMany is a nop
|
2018-09-22 01:00:10 +00:00
|
|
|
func (dag BaseDAGService) RemoveMany(ctx context.Context, keys []cid.Cid) error {
|
2018-08-07 18:01:02 +00:00
|
|
|
return nil
|
|
|
|
}
|