// startRaft starts a raft node from the given wal dir. // If the wal dir does not exist, startRaft will start a new raft node. // If the wal dir exists, startRaft will restart the previous raft node. // startRaft returns the started raft node and the opened wal. func startRaft(id int64, peerIDs []int64, waldir string) (raft.Node, *wal.WAL) { if !wal.Exist(waldir) { w, err := wal.Create(waldir) if err != nil { log.Fatal(err) } n := raft.Start(id, peerIDs, 10, 1) return n, w } // restart a node from previous wal // TODO(xiangli): check snapshot; not open from one w, err := wal.OpenAtIndex(waldir, 1) if err != nil { log.Fatal(err) } wid, st, ents, err := w.ReadAll() // TODO(xiangli): save/recovery nodeID? if wid != 0 { log.Fatalf("unexpected nodeid %d: nodeid should always be zero until we save nodeid into wal", wid) } if err != nil { log.Fatal(err) } n := raft.Restart(id, peerIDs, 10, 1, st, ents) return n, w }
func startNode(cfg *ServerConfig, ids []types.ID) (id types.ID, n raft.Node, s *raft.MemoryStorage, w *wal.WAL) { var err error member := cfg.Cluster.MemberByName(cfg.Name) metadata := pbutil.MustMarshal( &pb.Metadata{ NodeID: uint64(member.ID), ClusterID: uint64(cfg.Cluster.ID()), }, ) if err := os.MkdirAll(cfg.SnapDir(), privateDirMode); err != nil { log.Fatalf("etcdserver create snapshot directory error: %v", err) } if w, err = wal.Create(cfg.WALDir(), metadata); err != nil { log.Fatalf("etcdserver: create wal error: %v", err) } peers := make([]raft.Peer, len(ids)) for i, id := range ids { ctx, err := json.Marshal((*cfg.Cluster).Member(id)) if err != nil { log.Panicf("marshal member should never fail: %v", err) } peers[i] = raft.Peer{ID: uint64(id), Context: ctx} } id = member.ID log.Printf("etcdserver: start member %s in cluster %s", id, cfg.Cluster.ID()) s = raft.NewMemoryStorage() n = raft.StartNode(uint64(id), peers, 10, 1, s) return }
// handleBackup handles a request that intends to do a backup. func handleBackup(c *cli.Context) { srcSnap := path.Join(c.String("data-dir"), "member", "snap") destSnap := path.Join(c.String("backup-dir"), "member", "snap") srcWAL := path.Join(c.String("data-dir"), "member", "wal") destWAL := path.Join(c.String("backup-dir"), "member", "wal") if err := os.MkdirAll(destSnap, 0700); err != nil { log.Fatalf("failed creating backup snapshot dir %v: %v", destSnap, err) } ss := snap.New(srcSnap) snapshot, err := ss.Load() if err != nil && err != snap.ErrNoSnapshot { log.Fatal(err) } var walsnap walpb.Snapshot if snapshot != nil { walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term newss := snap.New(destSnap) if err = newss.SaveSnap(*snapshot); err != nil { log.Fatal(err) } } w, err := wal.OpenForRead(srcWAL, walsnap) if err != nil { log.Fatal(err) } defer w.Close() wmetadata, state, ents, err := w.ReadAll() switch err { case nil: case wal.ErrSnapshotNotFound: fmt.Printf("Failed to find the match snapshot record %+v in wal %v.", walsnap, srcWAL) fmt.Printf("etcdctl will add it back. Start auto fixing...") default: log.Fatal(err) } var metadata etcdserverpb.Metadata pbutil.MustUnmarshal(&metadata, wmetadata) idgen := idutil.NewGenerator(0, time.Now()) metadata.NodeID = idgen.Next() metadata.ClusterID = idgen.Next() neww, err := wal.Create(destWAL, pbutil.MustMarshal(&metadata)) if err != nil { log.Fatal(err) } defer neww.Close() if err := neww.Save(state, ents); err != nil { log.Fatal(err) } if err := neww.SaveSnapshot(walsnap); err != nil { log.Fatal(err) } }
// Create returns a new WAL object with the given encrypters and decrypters. func (wc walCryptor) Create(dirpath string, metadata []byte) (WAL, error) { w, err := wal.Create(dirpath, metadata) if err != nil { return nil, err } return &wrappedWAL{ WAL: w, encrypter: wc.encrypter, decrypter: wc.decrypter, }, nil }
func (n *Node) loadAndStart(ctx context.Context, forceNewCluster bool) error { walDir := n.walDir() snapDir := n.snapDir() if err := os.MkdirAll(snapDir, 0700); err != nil { return fmt.Errorf("create snapshot directory error: %v", err) } // Create a snapshotter n.snapshotter = snap.New(snapDir) if !wal.Exist(walDir) { raftNode := &api.RaftMember{ RaftID: n.Config.ID, Addr: n.Address, } metadata, err := raftNode.Marshal() if err != nil { return fmt.Errorf("error marshalling raft node: %v", err) } n.wal, err = wal.Create(walDir, metadata) if err != nil { return fmt.Errorf("create wal error: %v", err) } n.cluster.AddMember(&membership.Member{RaftMember: raftNode}) n.startNodePeers = []raft.Peer{{ID: n.Config.ID, Context: metadata}} return nil } // Load snapshot data snapshot, err := n.snapshotter.Load() if err != nil && err != snap.ErrNoSnapshot { return err } if snapshot != nil { // Load the snapshot data into the store if err := n.restoreFromSnapshot(snapshot.Data, forceNewCluster); err != nil { return err } } // Read logs to fully catch up store if err := n.readWAL(ctx, snapshot, forceNewCluster); err != nil { return err } n.Node = raft.RestartNode(n.Config) return nil }
// makeWAL creates a WAL for the initial cluster func makeWAL(waldir string, cl *membership.RaftCluster) { if err := os.MkdirAll(waldir, 0755); err != nil { ExitWithError(ExitIO, err) } m := cl.MemberByName(restoreName) md := &etcdserverpb.Metadata{NodeID: uint64(m.ID), ClusterID: uint64(cl.ID())} metadata, merr := md.Marshal() if merr != nil { ExitWithError(ExitInvalidInput, merr) } w, walerr := wal.Create(waldir, metadata) if walerr != nil { ExitWithError(ExitIO, walerr) } defer w.Close() peers := make([]raft.Peer, len(cl.MemberIDs())) for i, id := range cl.MemberIDs() { ctx, err := json.Marshal((*cl).Member(id)) if err != nil { ExitWithError(ExitInvalidInput, err) } peers[i] = raft.Peer{ID: uint64(id), Context: ctx} } ents := make([]raftpb.Entry, len(peers)) for i, p := range peers { cc := raftpb.ConfChange{ Type: raftpb.ConfChangeAddNode, NodeID: p.ID, Context: p.Context} d, err := cc.Marshal() if err != nil { ExitWithError(ExitInvalidInput, err) } e := raftpb.Entry{ Type: raftpb.EntryConfChange, Term: 1, Index: uint64(i + 1), Data: d, } ents[i] = e } w.Save(raftpb.HardState{ Term: 1, Vote: peers[0].ID, Commit: uint64(len(ents))}, ents) }
// handleBackup handles a request that intends to do a backup. func handleBackup(c *cli.Context) { srcSnap := path.Join(c.String("data-dir"), "snap") destSnap := path.Join(c.String("backup-dir"), "snap") srcWAL := path.Join(c.String("data-dir"), "wal") destWAL := path.Join(c.String("backup-dir"), "wal") if err := os.MkdirAll(destSnap, 0700); err != nil { log.Fatalf("failed creating backup snapshot dir %v: %v", destSnap, err) } ss := snap.New(srcSnap) snapshot, err := ss.Load() if err != nil && err != snap.ErrNoSnapshot { log.Fatal(err) } var index uint64 if snapshot != nil { index = snapshot.Metadata.Index newss := snap.New(destSnap) if err := newss.SaveSnap(*snapshot); err != nil { log.Fatal(err) } } w, err := wal.OpenNotInUse(srcWAL, index) if err != nil { log.Fatal(err) } defer w.Close() wmetadata, state, ents, err := w.ReadAll() if err != nil { log.Fatal(err) } var metadata etcdserverpb.Metadata pbutil.MustUnmarshal(&metadata, wmetadata) rand.Seed(time.Now().UnixNano()) metadata.NodeID = etcdserver.GenID() metadata.ClusterID = etcdserver.GenID() neww, err := wal.Create(destWAL, pbutil.MustMarshal(&metadata)) if err != nil { log.Fatal(err) } defer neww.Close() if err := neww.Save(state, ents); err != nil { log.Fatal(err) } }
// handleBackup handles a request that intends to do a backup. func handleBackup(c *cli.Context) { srcSnap := path.Join(c.String("data-dir"), "snap") destSnap := path.Join(c.String("backup-dir"), "snap") srcWAL := path.Join(c.String("data-dir"), "wal") destWAL := path.Join(c.String("backup-dir"), "wal") if err := os.MkdirAll(destSnap, 0700); err != nil { log.Fatalf("failed creating backup snapshot dir %v: %v", destSnap, err) } ss := snap.New(srcSnap) snapshot, err := ss.Load() if err != nil && err != snap.ErrNoSnapshot { log.Fatal(err) } var walsnap walpb.Snapshot if snapshot != nil { walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term newss := snap.New(destSnap) if err := newss.SaveSnap(*snapshot); err != nil { log.Fatal(err) } } w, err := wal.OpenNotInUse(srcWAL, walsnap) if err != nil { log.Fatal(err) } defer w.Close() wmetadata, state, ents, err := w.ReadAll() if err != nil { log.Fatal(err) } var metadata etcdserverpb.Metadata pbutil.MustUnmarshal(&metadata, wmetadata) idgen := idutil.NewGenerator(0, time.Now()) metadata.NodeID = idgen.Next() metadata.ClusterID = idgen.Next() neww, err := wal.Create(destWAL, pbutil.MustMarshal(&metadata)) if err != nil { log.Fatal(err) } defer neww.Close() if err := neww.Save(state, ents); err != nil { log.Fatal(err) } }
func startNode(cfg *ServerConfig, cl *cluster, ids []types.ID) (id types.ID, n raft.Node, s *raft.MemoryStorage, w *wal.WAL) { var err error member := cl.MemberByName(cfg.Name) metadata := pbutil.MustMarshal( &pb.Metadata{ NodeID: uint64(member.ID), ClusterID: uint64(cl.ID()), }, ) //创建记录 if err = os.MkdirAll(cfg.SnapDir(), privateDirMode); err != nil { plog.Fatalf("create snapshot directory error: %v", err) } if w, err = wal.Create(cfg.WALDir(), metadata); err != nil { plog.Fatalf("create wal error: %v", err) } //获取节点信息 peers := make([]raft.Peer, len(ids)) for i, id := range ids { ctx, err := json.Marshal((*cl).Member(id)) if err != nil { plog.Panicf("marshal member should never fail: %v", err) } peers[i] = raft.Peer{ID: uint64(id), Context: ctx} } id = member.ID plog.Infof("starting member %s in cluster %s", id, cl.ID()) s = raft.NewMemoryStorage() c := &raft.Config{ ID: uint64(id), ElectionTick: cfg.ElectionTicks, HeartbeatTick: 1, Storage: s, //存储 MaxSizePerMsg: maxSizePerMsg, MaxInflightMsgs: maxInflightMsgs, CheckQuorum: true, } n = raft.StartNode(c, peers) raftStatusMu.Lock() raftStatus = n.Status raftStatusMu.Unlock() advanceTicksForElection(n, c.ElectionTick) return }
func (n *Node) createWAL(nodeID string) (raft.Peer, error) { raftNode := &api.RaftMember{ RaftID: n.Config.ID, NodeID: nodeID, Addr: n.Address, } metadata, err := raftNode.Marshal() if err != nil { return raft.Peer{}, fmt.Errorf("error marshalling raft node: %v", err) } n.wal, err = wal.Create(n.walDir(), metadata) if err != nil { return raft.Peer{}, fmt.Errorf("create WAL error: %v", err) } n.cluster.AddMember(&membership.Member{RaftMember: raftNode}) return raft.Peer{ID: n.Config.ID, Context: metadata}, nil }
func (n *Node) createWAL(nodeID string) (raft.Peer, error) { raftNode := &api.RaftMember{ RaftID: n.Config.ID, NodeID: nodeID, Addr: n.opts.Addr, } metadata, err := raftNode.Marshal() if err != nil { return raft.Peer{}, errors.Wrap(err, "error marshalling raft node") } n.wal, err = wal.Create(n.walDir(), metadata) if err != nil { return raft.Peer{}, errors.Wrap(err, "failed to create WAL") } n.cluster.AddMember(&membership.Member{RaftMember: raftNode}) return raft.Peer{ID: n.Config.ID, Context: metadata}, nil }
// openWAL returns a WAL ready for reading. func (rc *raftNode) openWAL() *wal.WAL { if wal.Exist(rc.waldir) == false { if err := os.Mkdir(rc.waldir, 0750); err != nil { log.Fatalf("raftexample: cannot create dir for wal (%v)", err) } w, err := wal.Create(rc.waldir, nil) if err != nil { log.Fatalf("raftexample: create wal error (%v)", err) } w.Close() } w, err := wal.Open(rc.waldir, walpb.Snapshot{}) if err != nil { log.Fatalf("raftexample: error loading wal (%v)", err) } return w }
func startNode(cfg *ServerConfig, ids []uint64) (id uint64, n raft.Node, w *wal.WAL) { var err error // TODO: remove the discoveryURL when it becomes part of the source for // generating nodeID. member := cfg.Cluster.MemberByName(cfg.Name) metadata := pbutil.MustMarshal(&pb.Metadata{NodeID: member.ID, ClusterID: cfg.Cluster.ID()}) if w, err = wal.Create(cfg.WALDir(), metadata); err != nil { log.Fatal(err) } peers := make([]raft.Peer, len(ids)) for i, id := range ids { ctx, err := json.Marshal((*cfg.Cluster).Member(id)) if err != nil { log.Fatal(err) } peers[i] = raft.Peer{ID: id, Context: ctx} } id = member.ID log.Printf("etcdserver: start node %x in cluster %x", id, cfg.Cluster.ID()) n = raft.StartNode(id, peers, 10, 1) return }
func main() { flag.Parse() if len(*migrateDatadir) == 0 { glog.Fatal("need to set '--data-dir'") } dbpath := path.Join(*migrateDatadir, "member", "snap", "db") // etcd3 store backend. We will use it to parse v3 data files and extract information. be := backend.NewDefaultBackend(dbpath) tx := be.BatchTx() // etcd2 store backend. We will use v3 data to update this and then save snapshot to disk. st := store.New(etcdserver.StoreClusterPrefix, etcdserver.StoreKeysPrefix) expireTime := time.Now().Add(*ttl) tx.Lock() err := tx.UnsafeForEach([]byte("key"), func(k, v []byte) error { kv := &mvccpb.KeyValue{} kv.Unmarshal(v) // This is compact key. if !strings.HasPrefix(string(kv.Key), "/") { return nil } ttlOpt := store.TTLOptionSet{} if kv.Lease != 0 { ttlOpt = store.TTLOptionSet{ExpireTime: expireTime} } if !isTombstone(k) { sk := path.Join(strings.Trim(etcdserver.StoreKeysPrefix, "/"), string(kv.Key)) _, err := st.Set(sk, false, string(kv.Value), ttlOpt) if err != nil { return err } } else { st.Delete(string(kv.Key), false, false) } return nil }) if err != nil { glog.Fatal(err) } tx.Unlock() if err := traverseAndDeleteEmptyDir(st, "/"); err != nil { glog.Fatal(err) } // rebuild cluster state. metadata, hardstate, oldSt, err := rebuild(*migrateDatadir) if err != nil { glog.Fatal(err) } // In the following, it's low level logic that saves metadata and data into v2 snapshot. backupPath := *migrateDatadir + ".rollback.backup" if err := os.Rename(*migrateDatadir, backupPath); err != nil { glog.Fatal(err) } if err := os.MkdirAll(path.Join(*migrateDatadir, "member", "snap"), 0700); err != nil { glog.Fatal(err) } walDir := path.Join(*migrateDatadir, "member", "wal") w, err := wal.Create(walDir, metadata) if err != nil { glog.Fatal(err) } err = w.SaveSnapshot(walpb.Snapshot{Index: hardstate.Commit, Term: hardstate.Term}) if err != nil { glog.Fatal(err) } w.Close() event, err := oldSt.Get(etcdserver.StoreClusterPrefix, true, false) if err != nil { glog.Fatal(err) } // nodes (members info) for ConfState nodes := []uint64{} traverseMetadata(event.Node, func(n *store.NodeExtern) { if n.Key != etcdserver.StoreClusterPrefix { // update store metadata v := "" if !n.Dir { v = *n.Value } if n.Key == path.Join(etcdserver.StoreClusterPrefix, "version") { v = rollbackVersion } if _, err := st.Set(n.Key, n.Dir, v, store.TTLOptionSet{}); err != nil { glog.Fatal(err) } // update nodes fields := strings.Split(n.Key, "/") if len(fields) == 4 && fields[2] == "members" { nodeID, err := strconv.ParseUint(fields[3], 16, 64) if err != nil { glog.Fatalf("failed to parse member ID (%s): %v", fields[3], err) } nodes = append(nodes, nodeID) } } }) data, err := st.Save() if err != nil { glog.Fatal(err) } raftSnap := raftpb.Snapshot{ Data: data, Metadata: raftpb.SnapshotMetadata{ Index: hardstate.Commit, Term: hardstate.Term, ConfState: raftpb.ConfState{ Nodes: nodes, }, }, } snapshotter := snap.New(path.Join(*migrateDatadir, "member", "snap")) if err := snapshotter.SaveSnap(raftSnap); err != nil { glog.Fatal(err) } fmt.Println("Finished successfully") }
// makeWAL creates a WAL for the initial cluster func makeWALAndSnap(waldir, snapdir string, cl *membership.RaftCluster) { if err := fileutil.CreateDirAll(waldir); err != nil { ExitWithError(ExitIO, err) } // add members again to persist them to the store we create. st := store.New(etcdserver.StoreClusterPrefix, etcdserver.StoreKeysPrefix) cl.SetStore(st) for _, m := range cl.Members() { cl.AddMember(m) } m := cl.MemberByName(restoreName) md := &etcdserverpb.Metadata{NodeID: uint64(m.ID), ClusterID: uint64(cl.ID())} metadata, merr := md.Marshal() if merr != nil { ExitWithError(ExitInvalidInput, merr) } w, walerr := wal.Create(waldir, metadata) if walerr != nil { ExitWithError(ExitIO, walerr) } defer w.Close() peers := make([]raft.Peer, len(cl.MemberIDs())) for i, id := range cl.MemberIDs() { ctx, err := json.Marshal((*cl).Member(id)) if err != nil { ExitWithError(ExitInvalidInput, err) } peers[i] = raft.Peer{ID: uint64(id), Context: ctx} } ents := make([]raftpb.Entry, len(peers)) nodeIDs := make([]uint64, len(peers)) for i, p := range peers { nodeIDs[i] = p.ID cc := raftpb.ConfChange{ Type: raftpb.ConfChangeAddNode, NodeID: p.ID, Context: p.Context} d, err := cc.Marshal() if err != nil { ExitWithError(ExitInvalidInput, err) } e := raftpb.Entry{ Type: raftpb.EntryConfChange, Term: 1, Index: uint64(i + 1), Data: d, } ents[i] = e } commit, term := uint64(len(ents)), uint64(1) if err := w.Save(raftpb.HardState{ Term: term, Vote: peers[0].ID, Commit: commit}, ents); err != nil { ExitWithError(ExitIO, err) } b, berr := st.Save() if berr != nil { ExitWithError(ExitError, berr) } raftSnap := raftpb.Snapshot{ Data: b, Metadata: raftpb.SnapshotMetadata{ Index: commit, Term: term, ConfState: raftpb.ConfState{ Nodes: nodeIDs, }, }, } snapshotter := snap.New(snapdir) if err := snapshotter.SaveSnap(raftSnap); err != nil { panic(err) } if err := w.SaveSnapshot(walpb.Snapshot{Index: commit, Term: term}); err != nil { ExitWithError(ExitIO, err) } }
// NewServer creates a new EtcdServer from the supplied configuration. The // configuration is considered static for the lifetime of the EtcdServer. func NewServer(cfg *ServerConfig) *EtcdServer { m := cfg.Cluster.FindName(cfg.Name) if m == nil { // Should never happen log.Fatalf("could not find name %v in cluster!", cfg.Name) } snapdir := path.Join(cfg.DataDir, "snap") if err := os.MkdirAll(snapdir, privateDirMode); err != nil { log.Fatalf("etcdserver: cannot create snapshot directory: %v", err) } ss := snap.New(snapdir) st := store.New() var w *wal.WAL var n raft.Node var err error waldir := path.Join(cfg.DataDir, "wal") if !wal.Exist(waldir) { if w, err = wal.Create(waldir); err != nil { log.Fatal(err) } n = raft.StartNode(m.ID, cfg.Cluster.IDs(), 10, 1) } else { var index int64 snapshot, err := ss.Load() if err != nil && err != snap.ErrNoSnapshot { log.Fatal(err) } if snapshot != nil { log.Printf("etcdserver: restart from snapshot at index %d", snapshot.Index) st.Recovery(snapshot.Data) index = snapshot.Index } // restart a node from previous wal if w, err = wal.OpenAtIndex(waldir, index); err != nil { log.Fatal(err) } wid, st, ents, err := w.ReadAll() if err != nil { log.Fatal(err) } // TODO(xiangli): save/recovery nodeID? if wid != 0 { log.Fatalf("unexpected nodeid %d: nodeid should always be zero until we save nodeid into wal", wid) } n = raft.RestartNode(m.ID, cfg.Cluster.IDs(), 10, 1, snapshot, st, ents) } cls := NewClusterStore(st, *cfg.Cluster) s := &EtcdServer{ store: st, node: n, name: cfg.Name, storage: struct { *wal.WAL *snap.Snapshotter }{w, ss}, send: Sender(cfg.Transport, cls), clientURLs: cfg.ClientURLs, ticker: time.Tick(100 * time.Millisecond), syncTicker: time.Tick(500 * time.Millisecond), snapCount: cfg.SnapCount, ClusterStore: cls, } return s }
func Migrate4To5(dataDir string, name string) error { // prep new directories sd5 := snapDir5(dataDir) if err := os.MkdirAll(sd5, 0700); err != nil { return fmt.Errorf("failed creating snapshot directory %s: %v", sd5, err) } // read v0.4 data snap4, err := DecodeLatestSnapshot4FromDir(snapDir4(dataDir)) if err != nil { return err } cfg4, err := DecodeConfig4FromFile(cfgFile4(dataDir)) if err != nil { return err } ents4, err := DecodeLog4FromFile(logFile4(dataDir)) if err != nil { return err } nodeIDs := ents4.NodeIDs() nodeID := GuessNodeID(nodeIDs, snap4, cfg4, name) if nodeID == 0 { return fmt.Errorf("Couldn't figure out the node ID from the log or flags, cannot convert") } metadata := pbutil.MustMarshal(&pb.Metadata{NodeID: nodeID, ClusterID: 0x04add5}) wd5 := walDir5(dataDir) w, err := wal.Create(wd5, metadata) if err != nil { return fmt.Errorf("failed initializing wal at %s: %v", wd5, err) } defer w.Close() // transform v0.4 data var snap5 *raftpb.Snapshot if snap4 == nil { log.Printf("No snapshot found") } else { log.Printf("Found snapshot: lastIndex=%d", snap4.LastIndex) snap5 = snap4.Snapshot5() } st5 := cfg4.HardState5() // If we've got the most recent snapshot, we can use it's committed index. Still likely less than the current actual index, but worth it for the replay. if snap5 != nil { st5.Commit = snap5.Metadata.Index } ents5, err := Entries4To5(ents4) if err != nil { return err } ents5Len := len(ents5) log.Printf("Found %d log entries: firstIndex=%d lastIndex=%d", ents5Len, ents5[0].Index, ents5[ents5Len-1].Index) // explicitly prepend an empty entry as the WAL code expects it ents5 = append(make([]raftpb.Entry, 1), ents5...) if err = w.Save(st5, ents5); err != nil { return err } log.Printf("Log migration successful") // migrate snapshot (if necessary) and logs if snap5 != nil { ss := snap.New(sd5) if err := ss.SaveSnap(*snap5); err != nil { return err } log.Printf("Snapshot migration successful") } return nil }
func (o originalWAL) Create(dirpath string, metadata []byte) (WAL, error) { return wal.Create(dirpath, metadata) }
func Migrate4To2(dataDir string, name string) error { // prep new directories sd2 := snapDir2(dataDir) if err := os.MkdirAll(sd2, 0700); err != nil { return fmt.Errorf("failed creating snapshot directory %s: %v", sd2, err) } // read v0.4 data snap4, err := DecodeLatestSnapshot4FromDir(snapDir4(dataDir)) if err != nil { return err } cfg4, err := DecodeConfig4FromFile(cfgFile4(dataDir)) if err != nil { return err } ents4, err := DecodeLog4FromFile(logFile4(dataDir)) if err != nil { return err } nodeIDs := ents4.NodeIDs() nodeID := GuessNodeID(nodeIDs, snap4, cfg4, name) if nodeID == 0 { return fmt.Errorf("Couldn't figure out the node ID from the log or flags, cannot convert") } metadata := pbutil.MustMarshal(&pb.Metadata{NodeID: nodeID, ClusterID: 0x04add5}) wd2 := walDir2(dataDir) w, err := wal.Create(wd2, metadata) if err != nil { return fmt.Errorf("failed initializing wal at %s: %v", wd2, err) } defer w.Close() // transform v0.4 data var snap2 *raftpb.Snapshot if snap4 == nil { log.Printf("No snapshot found") } else { log.Printf("Found snapshot: lastIndex=%d", snap4.LastIndex) snap2 = snap4.Snapshot2() } st2 := cfg4.HardState2() // If we've got the most recent snapshot, we can use it's committed index. Still likely less than the current actual index, but worth it for the replay. if snap2 != nil { st2.Commit = snap2.Metadata.Index } ents2, err := Entries4To2(ents4) if err != nil { return err } ents2Len := len(ents2) log.Printf("Found %d log entries: firstIndex=%d lastIndex=%d", ents2Len, ents2[0].Index, ents2[ents2Len-1].Index) // set the state term to the biggest term we have ever seen, // so term of future entries will not be the same with term of old ones. st2.Term = ents2[ents2Len-1].Term // explicitly prepend an empty entry as the WAL code expects it ents2 = append(make([]raftpb.Entry, 1), ents2...) if err = w.Save(st2, ents2); err != nil { return err } log.Printf("Log migration successful") // migrate snapshot (if necessary) and logs var walsnap walpb.Snapshot if snap2 != nil { walsnap.Index, walsnap.Term = snap2.Metadata.Index, snap2.Metadata.Term ss := snap.New(sd2) if err := ss.SaveSnap(*snap2); err != nil { return err } log.Printf("Snapshot migration successful") } if err = w.SaveSnapshot(walsnap); err != nil { return err } return nil }