4922c95589
This allows to call the Rest API's status and sync endpoints with a "?local=true" parameter. This will trigger operations but only on the local peer. Cluster *Local and RPC-*Local methods have been accordingly, although they are aliases for the PinTracker methods (but otherwise they would not be exposed in external APIs). ipfs-cluster-ctl has been updated to support the new flag. The rationaly behind this feature is that sometimes, a single cluster peer (or the ipfs daemon in it) is misbehaving. The user then wants to Sync, Recover, or see Status for that single peer. This is specially relevant when working with big pinsets in larger clusters, as a Status() call will be considerably more expensive when broadcasted everywhere. Note that the Rest API keeps returning GlobalPinInfo objects even on local=true calls. This ensures that the user always gets the same datatype from an endpoint. License: MIT Signed-off-by: Hector Sanjuan <code@hector.link>
375 lines
8.5 KiB
Go
375 lines
8.5 KiB
Go
package test
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ipfs/ipfs-cluster/api"
|
|
|
|
rpc "github.com/hsanjuan/go-libp2p-gorpc"
|
|
cid "github.com/ipfs/go-cid"
|
|
host "github.com/libp2p/go-libp2p-host"
|
|
peer "github.com/libp2p/go-libp2p-peer"
|
|
)
|
|
|
|
// ErrBadCid is returned when using ErrorCid. Operations with that CID always
|
|
// fail.
|
|
var ErrBadCid = errors.New("this is an expected error when using ErrorCid")
|
|
|
|
type mockService struct{}
|
|
|
|
// NewMockRPCClient creates a mock ipfs-cluster RPC server and returns
|
|
// a client to it.
|
|
func NewMockRPCClient(t *testing.T) *rpc.Client {
|
|
return NewMockRPCClientWithHost(t, nil)
|
|
}
|
|
|
|
// NewMockRPCClientWithHost returns a mock ipfs-cluster RPC server
|
|
// initialized with a given host.
|
|
func NewMockRPCClientWithHost(t *testing.T, h host.Host) *rpc.Client {
|
|
s := rpc.NewServer(h, "mock")
|
|
c := rpc.NewClientWithServer(h, "mock", s)
|
|
err := s.RegisterName("Cluster", &mockService{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return c
|
|
}
|
|
|
|
func (mock *mockService) Pin(in api.PinSerial, out *struct{}) error {
|
|
if in.Cid == ErrorCid {
|
|
return ErrBadCid
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) Unpin(in api.PinSerial, out *struct{}) error {
|
|
if in.Cid == ErrorCid {
|
|
return ErrBadCid
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) Pins(in struct{}, out *[]api.PinSerial) error {
|
|
*out = []api.PinSerial{
|
|
{
|
|
Cid: TestCid1,
|
|
},
|
|
{
|
|
Cid: TestCid2,
|
|
},
|
|
{
|
|
Cid: TestCid3,
|
|
},
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) PinGet(in api.PinSerial, out *api.PinSerial) error {
|
|
if in.Cid == ErrorCid {
|
|
return errors.New("expected error when using ErrorCid")
|
|
}
|
|
*out = in
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) ID(in struct{}, out *api.IDSerial) error {
|
|
//_, pubkey, _ := crypto.GenerateKeyPair(
|
|
// DefaultConfigCrypto,
|
|
// DefaultConfigKeyLength)
|
|
*out = api.IDSerial{
|
|
ID: TestPeerID1.Pretty(),
|
|
//PublicKey: pubkey,
|
|
Version: "0.0.mock",
|
|
IPFS: api.IPFSIDSerial{
|
|
ID: TestPeerID1.Pretty(),
|
|
Addresses: api.MultiaddrsSerial{
|
|
api.MultiaddrSerial("/ip4/127.0.0.1/tcp/4001/ipfs/" + TestPeerID1.Pretty()),
|
|
},
|
|
},
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) Version(in struct{}, out *api.Version) error {
|
|
*out = api.Version{
|
|
Version: "0.0.mock",
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) Peers(in struct{}, out *[]api.IDSerial) error {
|
|
id := api.IDSerial{}
|
|
mock.ID(in, &id)
|
|
|
|
*out = []api.IDSerial{id}
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) PeerAdd(in api.MultiaddrSerial, out *api.IDSerial) error {
|
|
id := api.IDSerial{}
|
|
mock.ID(struct{}{}, &id)
|
|
*out = id
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) PeerRemove(in peer.ID, out *struct{}) error {
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) StatusAll(in struct{}, out *[]api.GlobalPinInfoSerial) error {
|
|
c1, _ := cid.Decode(TestCid1)
|
|
c2, _ := cid.Decode(TestCid2)
|
|
c3, _ := cid.Decode(TestCid3)
|
|
*out = globalPinInfoSliceToSerial([]api.GlobalPinInfo{
|
|
{
|
|
Cid: c1,
|
|
PeerMap: map[peer.ID]api.PinInfo{
|
|
TestPeerID1: {
|
|
Cid: c1,
|
|
Peer: TestPeerID1,
|
|
Status: api.TrackerStatusPinned,
|
|
TS: time.Now(),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Cid: c2,
|
|
PeerMap: map[peer.ID]api.PinInfo{
|
|
TestPeerID1: {
|
|
Cid: c2,
|
|
Peer: TestPeerID1,
|
|
Status: api.TrackerStatusPinning,
|
|
TS: time.Now(),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Cid: c3,
|
|
PeerMap: map[peer.ID]api.PinInfo{
|
|
TestPeerID1: {
|
|
Cid: c3,
|
|
Peer: TestPeerID1,
|
|
Status: api.TrackerStatusPinError,
|
|
TS: time.Now(),
|
|
},
|
|
},
|
|
},
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) StatusAllLocal(in struct{}, out *[]api.PinInfoSerial) error {
|
|
return mock.TrackerStatusAll(in, out)
|
|
}
|
|
|
|
func (mock *mockService) Status(in api.PinSerial, out *api.GlobalPinInfoSerial) error {
|
|
if in.Cid == ErrorCid {
|
|
return ErrBadCid
|
|
}
|
|
c1, _ := cid.Decode(TestCid1)
|
|
*out = api.GlobalPinInfo{
|
|
Cid: c1,
|
|
PeerMap: map[peer.ID]api.PinInfo{
|
|
TestPeerID1: {
|
|
Cid: c1,
|
|
Peer: TestPeerID1,
|
|
Status: api.TrackerStatusPinned,
|
|
TS: time.Now(),
|
|
},
|
|
},
|
|
}.ToSerial()
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) StatusLocal(in api.PinSerial, out *api.PinInfoSerial) error {
|
|
return mock.TrackerStatus(in, out)
|
|
}
|
|
|
|
func (mock *mockService) SyncAll(in struct{}, out *[]api.GlobalPinInfoSerial) error {
|
|
return mock.StatusAll(in, out)
|
|
}
|
|
|
|
func (mock *mockService) SyncAllLocal(in struct{}, out *[]api.PinInfoSerial) error {
|
|
return mock.StatusAllLocal(in, out)
|
|
}
|
|
|
|
func (mock *mockService) Sync(in api.PinSerial, out *api.GlobalPinInfoSerial) error {
|
|
return mock.Status(in, out)
|
|
}
|
|
|
|
func (mock *mockService) SyncLocal(in api.PinSerial, out *api.PinInfoSerial) error {
|
|
return mock.StatusLocal(in, out)
|
|
}
|
|
|
|
func (mock *mockService) StateSync(in struct{}, out *[]api.PinInfoSerial) error {
|
|
*out = make([]api.PinInfoSerial, 0, 0)
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) RecoverAllLocal(in struct{}, out *[]api.PinInfoSerial) error {
|
|
return mock.TrackerRecoverAll(in, out)
|
|
}
|
|
|
|
func (mock *mockService) Recover(in api.PinSerial, out *api.GlobalPinInfoSerial) error {
|
|
return mock.Status(in, out)
|
|
}
|
|
|
|
func (mock *mockService) RecoverLocal(in api.PinSerial, out *api.PinInfoSerial) error {
|
|
return mock.TrackerRecover(in, out)
|
|
}
|
|
|
|
/* Tracker methods */
|
|
|
|
func (mock *mockService) Track(in api.PinSerial, out *struct{}) error {
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) Untrack(in api.PinSerial, out *struct{}) error {
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) TrackerStatusAll(in struct{}, out *[]api.PinInfoSerial) error {
|
|
c1, _ := cid.Decode(TestCid1)
|
|
c3, _ := cid.Decode(TestCid3)
|
|
|
|
*out = pinInfoSliceToSerial([]api.PinInfo{
|
|
{
|
|
Cid: c1,
|
|
Peer: TestPeerID1,
|
|
Status: api.TrackerStatusPinned,
|
|
TS: time.Now(),
|
|
},
|
|
{
|
|
Cid: c3,
|
|
Peer: TestPeerID1,
|
|
Status: api.TrackerStatusPinError,
|
|
TS: time.Now(),
|
|
},
|
|
})
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) TrackerStatus(in api.PinSerial, out *api.PinInfoSerial) error {
|
|
if in.Cid == ErrorCid {
|
|
return ErrBadCid
|
|
}
|
|
c1, _ := cid.Decode(TestCid1)
|
|
|
|
*out = api.PinInfo{
|
|
Cid: c1,
|
|
Peer: TestPeerID2,
|
|
Status: api.TrackerStatusPinned,
|
|
TS: time.Now(),
|
|
}.ToSerial()
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) TrackerRecoverAll(in struct{}, out *[]api.PinInfoSerial) error {
|
|
*out = make([]api.PinInfoSerial, 0, 0)
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) TrackerRecover(in api.PinSerial, out *api.PinInfoSerial) error {
|
|
in2 := in.ToPin()
|
|
*out = api.PinInfo{
|
|
Cid: in2.Cid,
|
|
Peer: TestPeerID1,
|
|
Status: api.TrackerStatusPinned,
|
|
TS: time.Now(),
|
|
}.ToSerial()
|
|
return nil
|
|
}
|
|
|
|
/* PeerManager methods */
|
|
|
|
func (mock *mockService) PeerManagerAddPeer(in api.MultiaddrSerial, out *struct{}) error {
|
|
return nil
|
|
}
|
|
|
|
/* IPFSConnector methods */
|
|
|
|
func (mock *mockService) IPFSPin(in api.PinSerial, out *struct{}) error {
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) IPFSUnpin(in api.PinSerial, out *struct{}) error {
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) IPFSPinLsCid(in api.PinSerial, out *api.IPFSPinStatus) error {
|
|
if in.Cid == TestCid1 || in.Cid == TestCid3 {
|
|
*out = api.IPFSPinStatusRecursive
|
|
} else {
|
|
*out = api.IPFSPinStatusUnpinned
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) IPFSPinLs(in string, out *map[string]api.IPFSPinStatus) error {
|
|
m := map[string]api.IPFSPinStatus{
|
|
TestCid1: api.IPFSPinStatusRecursive,
|
|
TestCid3: api.IPFSPinStatusRecursive,
|
|
}
|
|
*out = m
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) IPFSConnectSwarms(in struct{}, out *struct{}) error {
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) IPFSConfigKey(in string, out *interface{}) error {
|
|
switch in {
|
|
case "Datastore/StorageMax":
|
|
*out = "100KB"
|
|
default:
|
|
return errors.New("configuration key not found")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) IPFSRepoSize(in struct{}, out *uint64) error {
|
|
// since we have two pins. Assume each is 1KB.
|
|
*out = 2000
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) IPFSFreeSpace(in struct{}, out *uint64) error {
|
|
// RepoSize is 2KB, StorageMax is 100KB
|
|
*out = 98000
|
|
return nil
|
|
}
|
|
|
|
func (mock *mockService) ConsensusAddPeer(in peer.ID, out *struct{}) error {
|
|
return errors.New("mock rpc cannot redirect")
|
|
}
|
|
|
|
func (mock *mockService) ConsensusRmPeer(in peer.ID, out *struct{}) error {
|
|
return errors.New("mock rpc cannot redirect")
|
|
}
|
|
|
|
func (mock *mockService) ConsensusPeers(in struct{}, out *[]peer.ID) error {
|
|
*out = []peer.ID{TestPeerID1, TestPeerID2, TestPeerID3}
|
|
return nil
|
|
}
|
|
|
|
// FIXME: dup from util.go
|
|
func globalPinInfoSliceToSerial(gpi []api.GlobalPinInfo) []api.GlobalPinInfoSerial {
|
|
gpis := make([]api.GlobalPinInfoSerial, len(gpi), len(gpi))
|
|
for i, v := range gpi {
|
|
gpis[i] = v.ToSerial()
|
|
}
|
|
return gpis
|
|
}
|
|
|
|
// FIXME: dup from util.go
|
|
func pinInfoSliceToSerial(pi []api.PinInfo) []api.PinInfoSerial {
|
|
pis := make([]api.PinInfoSerial, len(pi), len(pi))
|
|
for i, v := range pi {
|
|
pis[i] = v.ToSerial()
|
|
}
|
|
return pis
|
|
}
|