ipfs-cluster/raft.go
Hector Sanjuan 6c18c02106 Issue #10: peers/add and peers/rm feature + tests
This commit adds PeerAdd() and PeerRemove() endpoints, CLI support,
tests. Peer management is a delicate issue because of how the consensus
works underneath and the places that need to track such peers.

When adding a peer the procedure is as follows:

* Try to open a connection to the new peer and abort if not reachable
* Broadcast a PeerManagerAddPeer operation which tells all cluster members
to add the new Peer. The Raft leader will add it to Raft's peerset and
the multiaddress will be saved in the ClusterPeers configuration key.
* If the above fails because some cluster node is not responding,
broadcast a PeerRemove() and try to undo any damage.
* If the broadcast succeeds, send our ClusterPeers to the new Peer along with
the local multiaddress we are using in the connection opened in the
first step (that is the multiaddress through which the other peer can reach us)
* The new peer updates its configuration with the new list and joins
the consensus

License: MIT
Signed-off-by: Hector Sanjuan <hector@protocol.ai>
2017-02-02 13:51:49 +01:00

92 lines
2.7 KiB
Go

package ipfscluster
import (
"io/ioutil"
"path/filepath"
hashiraft "github.com/hashicorp/raft"
raftboltdb "github.com/hashicorp/raft-boltdb"
host "github.com/libp2p/go-libp2p-host"
peer "github.com/libp2p/go-libp2p-peer"
libp2praft "github.com/libp2p/go-libp2p-raft"
)
// libp2pRaftWrap wraps the stuff that we need to run
// hashicorp raft. We carry it around for convenience
type libp2pRaftWrap struct {
raft *hashiraft.Raft
transport *libp2praft.Libp2pTransport
snapshotStore hashiraft.SnapshotStore
logStore hashiraft.LogStore
stableStore hashiraft.StableStore
peerstore *libp2praft.Peerstore
boltdb *raftboltdb.BoltStore
}
// This function does all heavy the work which is specifically related to
// hashicorp's Raft. Other places should just rely on the Consensus interface.
func makeLibp2pRaft(cfg *Config, host host.Host, state State, op *clusterLogOp) (*libp2praft.Consensus, *libp2praft.Actor, *libp2pRaftWrap, error) {
logger.Debug("creating libp2p Raft transport")
transport, err := libp2praft.NewLibp2pTransportWithHost(host)
if err != nil {
logger.Error("creating libp2p-raft transport: ", err)
return nil, nil, nil, err
}
//logger.Debug("opening connections")
//transport.OpenConns()
pstore := &libp2praft.Peerstore{}
strPeers := []string{peer.IDB58Encode(host.ID())}
for _, addr := range cfg.ClusterPeers {
p, _, err := multiaddrSplit(addr)
if err != nil {
return nil, nil, nil, err
}
strPeers = append(strPeers, p.Pretty())
}
pstore.SetPeers(strPeers)
logger.Debug("creating OpLog")
cons := libp2praft.NewOpLog(state, op)
raftCfg := cfg.RaftConfig
if raftCfg == nil {
raftCfg = hashiraft.DefaultConfig()
raftCfg.EnableSingleNode = raftSingleMode
}
raftCfg.LogOutput = ioutil.Discard
raftCfg.Logger = raftStdLogger
logger.Debug("creating file snapshot store")
snapshots, err := hashiraft.NewFileSnapshotStoreWithLogger(cfg.ConsensusDataFolder, maxSnapshots, raftStdLogger)
if err != nil {
logger.Error("creating file snapshot store: ", err)
return nil, nil, nil, err
}
logger.Debug("creating BoltDB log store")
logStore, err := raftboltdb.NewBoltStore(filepath.Join(cfg.ConsensusDataFolder, "raft.db"))
if err != nil {
logger.Error("creating bolt store: ", err)
return nil, nil, nil, err
}
logger.Debug("creating Raft")
r, err := hashiraft.NewRaft(raftCfg, cons.FSM(), logStore, logStore, snapshots, pstore, transport)
if err != nil {
logger.Error("initializing raft: ", err)
return nil, nil, nil, err
}
return cons, libp2praft.NewActor(r), &libp2pRaftWrap{
raft: r,
transport: transport,
snapshotStore: snapshots,
logStore: logStore,
stableStore: logStore,
peerstore: pstore,
boltdb: logStore,
}, nil
}