2016-12-28 15:25:24 +00:00
|
|
|
// Package ipfscluster implements a wrapper for the IPFS deamon which
|
|
|
|
// allows to orchestrate pinning operations among several IPFS nodes.
|
2016-12-02 18:33:39 +00:00
|
|
|
//
|
2016-12-28 15:25:24 +00:00
|
|
|
// IPFS Cluster uses a go-libp2p-raft to keep a shared state between
|
2017-01-26 18:59:31 +00:00
|
|
|
// the different cluster peers. It also uses LibP2P to enable
|
2016-12-28 15:25:24 +00:00
|
|
|
// communication between its different components, which perform different
|
|
|
|
// tasks like managing the underlying IPFS daemons, or providing APIs for
|
|
|
|
// external control.
|
2016-12-02 18:33:39 +00:00
|
|
|
package ipfscluster
|
|
|
|
|
|
|
|
import (
|
|
|
|
"time"
|
|
|
|
|
2017-01-25 11:14:39 +00:00
|
|
|
rpc "github.com/hsanjuan/go-libp2p-gorpc"
|
2016-12-16 11:40:28 +00:00
|
|
|
cid "github.com/ipfs/go-cid"
|
2017-01-30 12:12:25 +00:00
|
|
|
crypto "github.com/libp2p/go-libp2p-crypto"
|
2016-12-16 11:40:28 +00:00
|
|
|
peer "github.com/libp2p/go-libp2p-peer"
|
2016-12-23 18:35:37 +00:00
|
|
|
protocol "github.com/libp2p/go-libp2p-protocol"
|
2017-01-26 18:59:31 +00:00
|
|
|
ma "github.com/multiformats/go-multiaddr"
|
2016-12-02 18:33:39 +00:00
|
|
|
)
|
|
|
|
|
2017-01-26 18:59:31 +00:00
|
|
|
// RPCProtocol is used to send libp2p messages between cluster peers
|
2017-01-24 15:19:23 +00:00
|
|
|
var RPCProtocol = protocol.ID("/ipfscluster/" + Version + "/rpc")
|
2016-12-23 18:35:37 +00:00
|
|
|
|
2017-01-25 17:07:19 +00:00
|
|
|
// TrackerStatus values
|
2016-12-28 15:25:24 +00:00
|
|
|
const (
|
|
|
|
// IPFSStatus should never take this value
|
2017-01-25 17:07:19 +00:00
|
|
|
TrackerStatusBug = iota
|
2017-01-23 23:52:42 +00:00
|
|
|
// The cluster node is offline or not responding
|
2017-01-25 17:07:19 +00:00
|
|
|
TrackerStatusClusterError
|
2016-12-28 15:25:24 +00:00
|
|
|
// An error occurred pinning
|
2017-01-25 17:07:19 +00:00
|
|
|
TrackerStatusPinError
|
2016-12-28 15:25:24 +00:00
|
|
|
// An error occurred unpinning
|
2017-01-25 17:07:19 +00:00
|
|
|
TrackerStatusUnpinError
|
2016-12-28 15:25:24 +00:00
|
|
|
// The IPFS daemon has pinned the item
|
2017-01-25 17:07:19 +00:00
|
|
|
TrackerStatusPinned
|
2016-12-28 15:25:24 +00:00
|
|
|
// The IPFS daemon is currently pinning the item
|
2017-01-25 17:07:19 +00:00
|
|
|
TrackerStatusPinning
|
2016-12-28 15:25:24 +00:00
|
|
|
// The IPFS daemon is currently unpinning the item
|
2017-01-25 17:07:19 +00:00
|
|
|
TrackerStatusUnpinning
|
2016-12-28 15:25:24 +00:00
|
|
|
// The IPFS daemon is not pinning the item
|
2017-01-25 17:07:19 +00:00
|
|
|
TrackerStatusUnpinned
|
2016-12-28 15:25:24 +00:00
|
|
|
// The IPFS deamon is not pinning the item but it is being tracked
|
2017-01-25 17:07:19 +00:00
|
|
|
TrackerStatusRemotePin
|
2016-12-28 15:25:24 +00:00
|
|
|
)
|
|
|
|
|
2017-01-25 17:07:19 +00:00
|
|
|
// TrackerStatus represents the status of a tracked Cid in the PinTracker
|
|
|
|
type TrackerStatus int
|
|
|
|
|
|
|
|
// IPFSPinStatus values
|
|
|
|
const (
|
|
|
|
IPFSPinStatusBug = iota
|
|
|
|
IPFSPinStatusError
|
|
|
|
IPFSPinStatusDirect
|
|
|
|
IPFSPinStatusRecursive
|
|
|
|
IPFSPinStatusIndirect
|
|
|
|
IPFSPinStatusUnpinned
|
|
|
|
)
|
|
|
|
|
|
|
|
// IPFSPinStatus represents the status of a pin in IPFS (direct, recursive etc.)
|
|
|
|
type IPFSPinStatus int
|
|
|
|
|
|
|
|
// IsPinned returns true if the status is Direct or Recursive
|
|
|
|
func (ips IPFSPinStatus) IsPinned() bool {
|
|
|
|
return ips == IPFSPinStatusDirect || ips == IPFSPinStatusRecursive
|
|
|
|
}
|
2016-12-28 15:25:24 +00:00
|
|
|
|
|
|
|
// GlobalPinInfo contains cluster-wide status information about a tracked Cid,
|
2017-01-26 18:59:31 +00:00
|
|
|
// indexed by cluster peer.
|
2016-12-28 15:25:24 +00:00
|
|
|
type GlobalPinInfo struct {
|
2017-01-25 17:07:19 +00:00
|
|
|
Cid *cid.Cid
|
|
|
|
PeerMap map[peer.ID]PinInfo
|
2016-12-28 15:25:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// PinInfo holds information about local pins. PinInfo is
|
|
|
|
// serialized when requesting the Global status, therefore
|
|
|
|
// we cannot use *cid.Cid.
|
|
|
|
type PinInfo struct {
|
|
|
|
CidStr string
|
|
|
|
Peer peer.ID
|
2017-01-25 17:07:19 +00:00
|
|
|
Status TrackerStatus
|
2016-12-28 15:25:24 +00:00
|
|
|
TS time.Time
|
2017-01-23 23:52:42 +00:00
|
|
|
Error string
|
2016-12-28 15:25:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// String converts an IPFSStatus into a readable string.
|
2017-01-25 17:07:19 +00:00
|
|
|
func (st TrackerStatus) String() string {
|
2016-12-28 15:25:24 +00:00
|
|
|
switch st {
|
2017-01-25 17:07:19 +00:00
|
|
|
case TrackerStatusBug:
|
2016-12-28 15:25:24 +00:00
|
|
|
return "bug"
|
2017-01-25 17:07:19 +00:00
|
|
|
case TrackerStatusClusterError:
|
2017-01-24 00:09:27 +00:00
|
|
|
return "cluster_error"
|
2017-01-25 17:07:19 +00:00
|
|
|
case TrackerStatusPinError:
|
2016-12-28 15:25:24 +00:00
|
|
|
return "pin_error"
|
2017-01-25 17:07:19 +00:00
|
|
|
case TrackerStatusUnpinError:
|
2016-12-28 15:25:24 +00:00
|
|
|
return "unpin_error"
|
2017-01-25 17:07:19 +00:00
|
|
|
case TrackerStatusPinned:
|
2016-12-28 15:25:24 +00:00
|
|
|
return "pinned"
|
2017-01-25 17:07:19 +00:00
|
|
|
case TrackerStatusPinning:
|
2016-12-28 15:25:24 +00:00
|
|
|
return "pinning"
|
2017-01-25 17:07:19 +00:00
|
|
|
case TrackerStatusUnpinning:
|
2016-12-28 15:25:24 +00:00
|
|
|
return "unpinning"
|
2017-01-25 17:07:19 +00:00
|
|
|
case TrackerStatusUnpinned:
|
2016-12-28 15:25:24 +00:00
|
|
|
return "unpinned"
|
2017-01-25 17:07:19 +00:00
|
|
|
case TrackerStatusRemotePin:
|
|
|
|
return "remote"
|
|
|
|
default:
|
|
|
|
return ""
|
2016-12-28 15:25:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-29 17:38:09 +00:00
|
|
|
// Component represents a piece of ipfscluster. Cluster components
|
2016-12-08 16:24:38 +00:00
|
|
|
// usually run their own goroutines (a http server for example). They
|
2016-12-23 18:35:37 +00:00
|
|
|
// communicate with the main Cluster component and other components
|
|
|
|
// (both local and remote), using an instance of rpc.Client.
|
2016-12-29 17:38:09 +00:00
|
|
|
type Component interface {
|
2016-12-23 18:35:37 +00:00
|
|
|
SetClient(*rpc.Client)
|
2016-12-02 18:33:39 +00:00
|
|
|
Shutdown() error
|
|
|
|
}
|
|
|
|
|
2016-12-15 18:08:46 +00:00
|
|
|
// API is a component which offers an API for Cluster. This is
|
2016-12-02 18:33:39 +00:00
|
|
|
// a base component.
|
2016-12-15 18:08:46 +00:00
|
|
|
type API interface {
|
2016-12-29 17:38:09 +00:00
|
|
|
Component
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// IPFSConnector is a component which allows cluster to interact with
|
2016-12-08 16:24:38 +00:00
|
|
|
// an IPFS daemon. This is a base component.
|
2016-12-02 18:33:39 +00:00
|
|
|
type IPFSConnector interface {
|
2016-12-29 17:38:09 +00:00
|
|
|
Component
|
2017-01-26 18:59:31 +00:00
|
|
|
ID() (IPFSID, error)
|
2016-12-02 18:33:39 +00:00
|
|
|
Pin(*cid.Cid) error
|
|
|
|
Unpin(*cid.Cid) error
|
2017-01-25 17:07:19 +00:00
|
|
|
PinLsCid(*cid.Cid) (IPFSPinStatus, error)
|
|
|
|
PinLs() (map[string]IPFSPinStatus, error)
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Peered represents a component which needs to be aware of the peers
|
2016-12-08 16:24:38 +00:00
|
|
|
// in the Cluster and of any changes to the peer set.
|
2016-12-02 18:33:39 +00:00
|
|
|
type Peered interface {
|
|
|
|
AddPeer(p peer.ID)
|
|
|
|
RmPeer(p peer.ID)
|
2017-01-30 12:12:25 +00:00
|
|
|
//SetPeers(peers []peer.ID)
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
|
|
|
|
2016-12-15 18:08:46 +00:00
|
|
|
// State represents the shared state of the cluster and it
|
|
|
|
// is used by the Consensus component to keep track of
|
2016-12-06 21:29:59 +00:00
|
|
|
// objects which objects are pinned. This component should be thread safe.
|
2016-12-15 18:08:46 +00:00
|
|
|
type State interface {
|
|
|
|
// AddPin adds a pin to the State
|
2016-12-06 21:29:59 +00:00
|
|
|
AddPin(*cid.Cid) error
|
2016-12-15 18:08:46 +00:00
|
|
|
// RmPin removes a pin from the State
|
2016-12-06 21:29:59 +00:00
|
|
|
RmPin(*cid.Cid) error
|
2016-12-09 19:54:46 +00:00
|
|
|
// ListPins lists all the pins in the state
|
|
|
|
ListPins() []*cid.Cid
|
2016-12-20 18:51:13 +00:00
|
|
|
// HasPin returns true if the state is holding a Cid
|
|
|
|
HasPin(*cid.Cid) bool
|
2017-02-02 22:52:06 +00:00
|
|
|
// AddPeer adds a peer to the shared state
|
2016-12-06 21:29:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// PinTracker represents a component which tracks the status of
|
|
|
|
// the pins in this cluster and ensures they are in sync with the
|
|
|
|
// IPFS daemon. This component should be thread safe.
|
|
|
|
type PinTracker interface {
|
2016-12-29 17:38:09 +00:00
|
|
|
Component
|
2016-12-19 17:35:24 +00:00
|
|
|
// Track tells the tracker that a Cid is now under its supervision
|
|
|
|
// The tracker may decide to perform an IPFS pin.
|
|
|
|
Track(*cid.Cid) error
|
|
|
|
// Untrack tells the tracker that a Cid is to be forgotten. The tracker
|
|
|
|
// may perform an IPFS unpin operation.
|
|
|
|
Untrack(*cid.Cid) error
|
2017-01-25 18:38:23 +00:00
|
|
|
// StatusAll returns the list of pins with their local status.
|
|
|
|
StatusAll() []PinInfo
|
|
|
|
// Status returns the local status of a given Cid.
|
|
|
|
Status(*cid.Cid) PinInfo
|
|
|
|
// SyncAll makes sure that all tracked Cids reflect the real IPFS status.
|
2017-01-25 17:07:19 +00:00
|
|
|
// It returns the list of pins which were updated by the call.
|
2017-01-25 18:38:23 +00:00
|
|
|
SyncAll() ([]PinInfo, error)
|
|
|
|
// Sync makes sure that the Cid status reflect the real IPFS status.
|
|
|
|
// It returns the local status of the Cid.
|
|
|
|
Sync(*cid.Cid) (PinInfo, error)
|
2016-12-20 18:51:13 +00:00
|
|
|
// Recover retriggers a Pin/Unpin operation in Cids with error status.
|
2017-01-25 18:38:23 +00:00
|
|
|
Recover(*cid.Cid) (PinInfo, error)
|
2016-12-02 18:33:39 +00:00
|
|
|
}
|
2017-01-26 18:59:31 +00:00
|
|
|
|
|
|
|
// IPFSID is used to store information about the underlying IPFS daemon
|
|
|
|
type IPFSID struct {
|
|
|
|
ID peer.ID
|
|
|
|
Addresses []ma.Multiaddr
|
|
|
|
Error string
|
|
|
|
}
|
|
|
|
|
|
|
|
// IPFSIDSerial is the serializable IPFSID for RPC requests
|
|
|
|
type IPFSIDSerial struct {
|
|
|
|
ID string
|
2017-01-30 12:12:25 +00:00
|
|
|
Addresses MultiaddrsSerial
|
2017-01-26 18:59:31 +00:00
|
|
|
Error string
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToSerial converts IPFSID to a go serializable object
|
|
|
|
func (id *IPFSID) ToSerial() IPFSIDSerial {
|
|
|
|
return IPFSIDSerial{
|
|
|
|
ID: peer.IDB58Encode(id.ID),
|
2017-01-30 12:12:25 +00:00
|
|
|
Addresses: MultiaddrsToSerial(id.Addresses),
|
2017-01-26 18:59:31 +00:00
|
|
|
Error: id.Error,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToID converts an IPFSIDSerial to IPFSID
|
|
|
|
// It will ignore any errors when parsing the fields.
|
|
|
|
func (ids *IPFSIDSerial) ToID() IPFSID {
|
|
|
|
id := IPFSID{}
|
|
|
|
if pID, err := peer.IDB58Decode(ids.ID); err == nil {
|
|
|
|
id.ID = pID
|
|
|
|
}
|
2017-01-30 12:12:25 +00:00
|
|
|
id.Addresses = ids.Addresses.ToMultiaddrs()
|
2017-01-26 18:59:31 +00:00
|
|
|
id.Error = ids.Error
|
|
|
|
return id
|
|
|
|
}
|
|
|
|
|
|
|
|
// ID holds information about the Cluster peer
|
|
|
|
type ID struct {
|
|
|
|
ID peer.ID
|
|
|
|
PublicKey crypto.PubKey
|
|
|
|
Addresses []ma.Multiaddr
|
2017-01-30 12:12:25 +00:00
|
|
|
ClusterPeers []ma.Multiaddr
|
2017-01-26 18:59:31 +00:00
|
|
|
Version string
|
|
|
|
Commit string
|
|
|
|
RPCProtocolVersion protocol.ID
|
|
|
|
Error string
|
|
|
|
IPFS IPFSID
|
|
|
|
}
|
|
|
|
|
|
|
|
// IDSerial is the serializable ID counterpart for RPC requests
|
|
|
|
type IDSerial struct {
|
|
|
|
ID string
|
|
|
|
PublicKey []byte
|
2017-01-30 12:12:25 +00:00
|
|
|
Addresses MultiaddrsSerial
|
|
|
|
ClusterPeers MultiaddrsSerial
|
2017-01-26 18:59:31 +00:00
|
|
|
Version string
|
|
|
|
Commit string
|
|
|
|
RPCProtocolVersion string
|
|
|
|
Error string
|
|
|
|
IPFS IPFSIDSerial
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToSerial converts an ID to its Go-serializable version
|
|
|
|
func (id ID) ToSerial() IDSerial {
|
2017-01-30 12:12:25 +00:00
|
|
|
var pkey []byte
|
|
|
|
if id.PublicKey != nil {
|
|
|
|
pkey, _ = id.PublicKey.Bytes()
|
2017-01-26 18:59:31 +00:00
|
|
|
}
|
2017-01-30 12:12:25 +00:00
|
|
|
|
2017-01-26 18:59:31 +00:00
|
|
|
return IDSerial{
|
|
|
|
ID: peer.IDB58Encode(id.ID),
|
|
|
|
PublicKey: pkey,
|
2017-01-30 12:12:25 +00:00
|
|
|
Addresses: MultiaddrsToSerial(id.Addresses),
|
|
|
|
ClusterPeers: MultiaddrsToSerial(id.ClusterPeers),
|
2017-01-26 18:59:31 +00:00
|
|
|
Version: id.Version,
|
|
|
|
Commit: id.Commit,
|
|
|
|
RPCProtocolVersion: string(id.RPCProtocolVersion),
|
|
|
|
Error: id.Error,
|
|
|
|
IPFS: id.IPFS.ToSerial(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToID converts an IDSerial object to ID.
|
|
|
|
// It will ignore any errors when parsing the fields.
|
|
|
|
func (ids IDSerial) ToID() ID {
|
|
|
|
id := ID{}
|
|
|
|
if pID, err := peer.IDB58Decode(ids.ID); err == nil {
|
|
|
|
id.ID = pID
|
|
|
|
}
|
|
|
|
if pkey, err := crypto.UnmarshalPublicKey(ids.PublicKey); err == nil {
|
|
|
|
id.PublicKey = pkey
|
|
|
|
}
|
2017-01-30 12:12:25 +00:00
|
|
|
|
|
|
|
id.Addresses = ids.Addresses.ToMultiaddrs()
|
|
|
|
id.ClusterPeers = ids.ClusterPeers.ToMultiaddrs()
|
2017-01-26 18:59:31 +00:00
|
|
|
id.Version = ids.Version
|
|
|
|
id.Commit = ids.Commit
|
|
|
|
id.RPCProtocolVersion = protocol.ID(ids.RPCProtocolVersion)
|
|
|
|
id.Error = ids.Error
|
|
|
|
id.IPFS = ids.IPFS.ToID()
|
|
|
|
return id
|
|
|
|
}
|
2017-01-30 12:12:25 +00:00
|
|
|
|
|
|
|
// MultiaddrSerial is a Multiaddress in a serializable form
|
|
|
|
type MultiaddrSerial []byte
|
|
|
|
|
|
|
|
// MultiaddrsSerial is an array of Multiaddresses in serializable form
|
|
|
|
type MultiaddrsSerial []MultiaddrSerial
|
|
|
|
|
|
|
|
// MultiaddrToSerial converts a Multiaddress to its serializable form
|
|
|
|
func MultiaddrToSerial(addr ma.Multiaddr) MultiaddrSerial {
|
|
|
|
return addr.Bytes()
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToMultiaddr converts a serializable Multiaddress to its original type.
|
|
|
|
// All errors are ignored.
|
|
|
|
func (addrS MultiaddrSerial) ToMultiaddr() ma.Multiaddr {
|
|
|
|
a, _ := ma.NewMultiaddrBytes(addrS)
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
|
|
|
|
// MultiaddrsToSerial converts a slice of Multiaddresses to its
|
|
|
|
// serializable form.
|
|
|
|
func MultiaddrsToSerial(addrs []ma.Multiaddr) MultiaddrsSerial {
|
|
|
|
addrsS := make([]MultiaddrSerial, len(addrs), len(addrs))
|
|
|
|
for i, a := range addrs {
|
|
|
|
addrsS[i] = MultiaddrToSerial(a)
|
|
|
|
}
|
|
|
|
return addrsS
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToMultiaddrs converts MultiaddrsSerial back to a slice of Multiaddresses
|
|
|
|
func (addrsS MultiaddrsSerial) ToMultiaddrs() []ma.Multiaddr {
|
|
|
|
addrs := make([]ma.Multiaddr, len(addrsS), len(addrsS))
|
|
|
|
for i, addrS := range addrsS {
|
|
|
|
addrs[i] = addrS.ToMultiaddr()
|
|
|
|
}
|
|
|
|
return addrs
|
|
|
|
}
|