// writeFakeRaftData writes the given snapshot and some generated WAL data to given "snap" and "wal" directories func writeFakeRaftData(t *testing.T, stateDir string, snapshot *raftpb.Snapshot, wf storage.WALFactory, sf storage.SnapFactory) { snapDir := filepath.Join(stateDir, "raft", "snap-v3-encrypted") walDir := filepath.Join(stateDir, "raft", "wal-v3-encrypted") require.NoError(t, os.MkdirAll(snapDir, 0755)) wsn := walpb.Snapshot{} if snapshot != nil { require.NoError(t, sf.New(snapDir).SaveSnap(*snapshot)) wsn.Index = snapshot.Metadata.Index wsn.Term = snapshot.Metadata.Term } var entries []raftpb.Entry for i := wsn.Index + 1; i < wsn.Index+6; i++ { entries = append(entries, raftpb.Entry{ Term: wsn.Term + 1, Index: i, Data: []byte(fmt.Sprintf("v3Entry %d", i)), }) } walWriter, err := wf.Create(walDir, []byte("v3metadata")) require.NoError(t, err) require.NoError(t, walWriter.SaveSnapshot(wsn)) require.NoError(t, walWriter.Save(raftpb.HardState{}, entries)) require.NoError(t, walWriter.Close()) }
func decryptRaftData(swarmdir, outdir, unlockKey string) error { krw, err := getKRW(swarmdir, unlockKey) if err != nil { return err } deks, err := getDEKData(krw) if err != nil { return err } _, d := encryption.Defaults(deks.CurrentDEK) if deks.PendingDEK == nil { _, d2 := encryption.Defaults(deks.PendingDEK) d = storage.MultiDecrypter{d, d2} } snapDir := filepath.Join(outdir, "snap-decrypted") if err := moveDirAside(snapDir); err != nil { return err } if err := storage.MigrateSnapshot( filepath.Join(swarmdir, "raft", "snap-v3-encrypted"), snapDir, storage.NewSnapFactory(encryption.NoopCrypter, d), storage.OriginalSnap); err != nil { return err } var walsnap walpb.Snapshot snap, err := storage.OriginalSnap.New(snapDir).Load() if err != nil && !os.IsNotExist(err) { return err } if snap != nil { walsnap.Index = snap.Metadata.Index walsnap.Term = snap.Metadata.Term } walDir := filepath.Join(outdir, "wal-decrypted") if err := moveDirAside(walDir); err != nil { return err } return storage.MigrateWALs(context.Background(), filepath.Join(swarmdir, "raft", "wal-v3-encrypted"), walDir, storage.NewWALFactory(encryption.NoopCrypter, d), storage.OriginalWAL, walsnap) }
func main() { from := flag.String("data-dir", "", "") snapfile := flag.String("start-snap", "", "The base name of snapshot file to start dumping") index := flag.Uint64("start-index", 0, "The index to start dumping") flag.Parse() if *from == "" { log.Fatal("Must provide -data-dir flag.") } if *snapfile != "" && *index != 0 { log.Fatal("start-snap and start-index flags cannot be used together.") } var ( walsnap walpb.Snapshot snapshot *raftpb.Snapshot err error ) isIndex := *index != 0 if isIndex { fmt.Printf("Start dumping log entries from index %d.\n", *index) walsnap.Index = *index } else { if *snapfile == "" { ss := snap.New(snapDir(*from)) snapshot, err = ss.Load() } else { snapshot, err = snap.Read(path.Join(snapDir(*from), *snapfile)) } switch err { case nil: walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term nodes := genIDSlice(snapshot.Metadata.ConfState.Nodes) fmt.Printf("Snapshot:\nterm=%d index=%d nodes=%s\n", walsnap.Term, walsnap.Index, nodes) case snap.ErrNoSnapshot: fmt.Printf("Snapshot:\nempty\n") default: log.Fatalf("Failed loading snapshot: %v", err) } fmt.Println("Start dupmping log entries from snapshot.") } w, err := wal.OpenForRead(walDir(*from), walsnap) if err != nil { log.Fatalf("Failed opening WAL: %v", err) } wmetadata, state, ents, err := w.ReadAll() w.Close() if err != nil && (!isIndex || err != wal.ErrSnapshotNotFound) { log.Fatalf("Failed reading WAL: %v", err) } id, cid := parseWALMetadata(wmetadata) vid := types.ID(state.Vote) fmt.Printf("WAL metadata:\nnodeID=%s clusterID=%s term=%d commitIndex=%d vote=%s\n", id, cid, state.Term, state.Commit, vid) fmt.Printf("WAL entries:\n") fmt.Printf("lastIndex=%d\n", ents[len(ents)-1].Index) fmt.Printf("%4s\t%10s\ttype\tdata\n", "term", "index") for _, e := range ents { msg := fmt.Sprintf("%4d\t%10d", e.Term, e.Index) switch e.Type { case raftpb.EntryNormal: msg = fmt.Sprintf("%s\tnorm", msg) var rr etcdserverpb.InternalRaftRequest if err := rr.Unmarshal(e.Data); err == nil { msg = fmt.Sprintf("%s\t%s", msg, rr.String()) break } var r etcdserverpb.Request if err := r.Unmarshal(e.Data); err == nil { switch r.Method { case "": msg = fmt.Sprintf("%s\tnoop", msg) case "SYNC": msg = fmt.Sprintf("%s\tmethod=SYNC time=%q", msg, time.Unix(0, r.Time)) case "QGET", "DELETE": msg = fmt.Sprintf("%s\tmethod=%s path=%s", msg, r.Method, excerpt(r.Path, 64, 64)) default: msg = fmt.Sprintf("%s\tmethod=%s path=%s val=%s", msg, r.Method, excerpt(r.Path, 64, 64), excerpt(r.Val, 128, 0)) } break } msg = fmt.Sprintf("%s\t???", msg) case raftpb.EntryConfChange: msg = fmt.Sprintf("%s\tconf", msg) var r raftpb.ConfChange if err := r.Unmarshal(e.Data); err != nil { msg = fmt.Sprintf("%s\t???", msg) } else { msg = fmt.Sprintf("%s\tmethod=%s id=%s", msg, r.Type, types.ID(r.NodeID)) } } fmt.Println(msg) } }
func (n *Node) readWAL(ctx context.Context, snapshot *raftpb.Snapshot, forceNewCluster bool) (err error) { var ( walsnap walpb.Snapshot metadata []byte st raftpb.HardState ents []raftpb.Entry ) if snapshot != nil { walsnap.Index = snapshot.Metadata.Index walsnap.Term = snapshot.Metadata.Term } repaired := false for { if n.wal, err = wal.Open(n.walDir(), walsnap); err != nil { return fmt.Errorf("open WAL error: %v", err) } if metadata, st, ents, err = n.wal.ReadAll(); err != nil { if err := n.wal.Close(); err != nil { return err } // we can only repair ErrUnexpectedEOF and we never repair twice. if repaired || err != io.ErrUnexpectedEOF { return fmt.Errorf("read WAL error (%v) and cannot be repaired", err) } if !wal.Repair(n.walDir()) { return fmt.Errorf("WAL error (%v) cannot be repaired", err) } log.G(ctx).Infof("repaired WAL error (%v)", err) repaired = true continue } break } defer func() { if err != nil { if walErr := n.wal.Close(); walErr != nil { n.Config.Logger.Errorf("error closing raft WAL: %v", walErr) } } }() var raftNode api.RaftMember if err := raftNode.Unmarshal(metadata); err != nil { return fmt.Errorf("error unmarshalling WAL metadata: %v", err) } n.Config.ID = raftNode.RaftID // All members that are no longer part of the cluster must be added to // the removed list right away, so that we don't try to connect to them // before processing the configuration change entries, which could make // us get stuck. for _, ent := range ents { if ent.Index <= st.Commit && ent.Type == raftpb.EntryConfChange { var cc raftpb.ConfChange if err := cc.Unmarshal(ent.Data); err != nil { return fmt.Errorf("error unmarshalling config change: %v", err) } if cc.Type == raftpb.ConfChangeRemoveNode { n.cluster.RemoveMember(cc.NodeID) } } } if forceNewCluster { // discard the previously uncommitted entries for i, ent := range ents { if ent.Index > st.Commit { log.G(context.Background()).Infof("discarding %d uncommitted WAL entries ", len(ents)-i) ents = ents[:i] break } } // force append the configuration change entries toAppEnts := createConfigChangeEnts(getIDs(snapshot, ents), uint64(n.Config.ID), st.Term, st.Commit) // All members that are being removed as part of the // force-new-cluster process must be added to the // removed list right away, so that we don't try to // connect to them before processing the configuration // change entries, which could make us get stuck. for _, ccEnt := range toAppEnts { if ccEnt.Type == raftpb.EntryConfChange { var cc raftpb.ConfChange if err := cc.Unmarshal(ccEnt.Data); err != nil { return fmt.Errorf("error unmarshalling force-new-cluster config change: %v", err) } if cc.Type == raftpb.ConfChangeRemoveNode { n.cluster.RemoveMember(cc.NodeID) } } } ents = append(ents, toAppEnts...) // force commit newly appended entries err := n.wal.Save(st, toAppEnts) if err != nil { log.G(context.Background()).Fatalf("%v", err) } if len(toAppEnts) != 0 { st.Commit = toAppEnts[len(toAppEnts)-1].Index } } if snapshot != nil { if err := n.raftStore.ApplySnapshot(*snapshot); err != nil { return err } } if err := n.raftStore.SetHardState(st); err != nil { return err } if err := n.raftStore.Append(ents); err != nil { return err } return nil }
func (n *Node) readWAL(ctx context.Context, snapshot *raftpb.Snapshot, forceNewCluster bool) (err error) { var ( walsnap walpb.Snapshot metadata []byte st raftpb.HardState ents []raftpb.Entry ) if snapshot != nil { walsnap.Index = snapshot.Metadata.Index walsnap.Term = snapshot.Metadata.Term } repaired := false for { if n.wal, err = wal.Open(n.walDir(), walsnap); err != nil { return fmt.Errorf("open wal error: %v", err) } if metadata, st, ents, err = n.wal.ReadAll(); err != nil { if err := n.wal.Close(); err != nil { return err } // we can only repair ErrUnexpectedEOF and we never repair twice. if repaired || err != io.ErrUnexpectedEOF { return fmt.Errorf("read wal error (%v) and cannot be repaired", err) } if !wal.Repair(n.walDir()) { return fmt.Errorf("WAL error (%v) cannot be repaired", err) } log.G(ctx).Infof("repaired WAL error (%v)", err) repaired = true continue } break } defer func() { if err != nil { if walErr := n.wal.Close(); walErr != nil { n.Config.Logger.Errorf("error closing raft WAL: %v", walErr) } } }() var raftNode api.RaftMember if err := raftNode.Unmarshal(metadata); err != nil { return fmt.Errorf("error unmarshalling wal metadata: %v", err) } n.Config.ID = raftNode.RaftID if forceNewCluster { // discard the previously uncommitted entries for i, ent := range ents { if ent.Index > st.Commit { log.G(context.Background()).Infof("discarding %d uncommitted WAL entries ", len(ents)-i) ents = ents[:i] break } } // force append the configuration change entries toAppEnts := createConfigChangeEnts(getIDs(snapshot, ents), uint64(n.Config.ID), st.Term, st.Commit) ents = append(ents, toAppEnts...) // force commit newly appended entries err := n.wal.Save(st, toAppEnts) if err != nil { log.G(context.Background()).Fatalf("%v", err) } if len(toAppEnts) != 0 { st.Commit = toAppEnts[len(toAppEnts)-1].Index } } if snapshot != nil { if err := n.raftStore.ApplySnapshot(*snapshot); err != nil { return err } } if err := n.raftStore.SetHardState(st); err != nil { return err } if err := n.raftStore.Append(ents); err != nil { return err } return nil }
// BootstrapFromDisk creates a new snapshotter and wal, and also reads the latest snapshot and WALs from disk func (e *EncryptedRaftLogger) BootstrapFromDisk(ctx context.Context, oldEncryptionKeys ...[]byte) (*raftpb.Snapshot, WALData, error) { e.encoderMu.Lock() defer e.encoderMu.Unlock() walDir := e.walDir() snapDir := e.snapDir() encrypter, decrypter := encryption.Defaults(e.EncryptionKey) if oldEncryptionKeys != nil { decrypters := []encryption.Decrypter{decrypter} for _, key := range oldEncryptionKeys { _, d := encryption.Defaults(key) decrypters = append(decrypters, d) } decrypter = MultiDecrypter(decrypters) } snapFactory := NewSnapFactory(encrypter, decrypter) if !fileutil.Exist(snapDir) { // If snapshots created by the etcd-v2 code exist, or by swarmkit development version, // read the latest snapshot and write it encoded to the new path. The new path // prevents etc-v2 creating snapshots that are visible to us, but not encoded and // out of sync with our WALs, after a downgrade. for _, dirs := range versionedWALSnapDirs[1:] { legacySnapDir := filepath.Join(e.StateDir, dirs.snap) if fileutil.Exist(legacySnapDir) { if err := MigrateSnapshot(legacySnapDir, snapDir, OriginalSnap, snapFactory); err != nil { return nil, WALData{}, err } break } } } // ensure the new directory exists if err := os.MkdirAll(snapDir, 0700); err != nil { return nil, WALData{}, errors.Wrap(err, "failed to create snapshot directory") } var ( snapshotter Snapshotter walObj WAL err error ) // Create a snapshotter and load snapshot data snapshotter = snapFactory.New(snapDir) snapshot, err := snapshotter.Load() if err != nil && err != snap.ErrNoSnapshot { return nil, WALData{}, err } walFactory := NewWALFactory(encrypter, decrypter) var walsnap walpb.Snapshot if snapshot != nil { walsnap.Index = snapshot.Metadata.Index walsnap.Term = snapshot.Metadata.Term } if !wal.Exist(walDir) { var walExists bool // If wals created by the etcd-v2 wal code exist, read the latest ones based // on this snapshot and encode them to wals in the new path to avoid adding // backwards-incompatible entries to those files. for _, dirs := range versionedWALSnapDirs[1:] { legacyWALDir := filepath.Join(e.StateDir, dirs.wal) if !wal.Exist(legacyWALDir) { continue } if err = MigrateWALs(ctx, legacyWALDir, walDir, OriginalWAL, walFactory, walsnap); err != nil { return nil, WALData{}, err } walExists = true break } if !walExists { return nil, WALData{}, ErrNoWAL } } walObj, waldata, err := ReadRepairWAL(ctx, walDir, walsnap, walFactory) if err != nil { return nil, WALData{}, err } e.snapshotter = snapshotter e.wal = walObj return snapshot, waldata, nil }