github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/helper/raftutil/fsm.go (about) 1 package raftutil 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 8 "github.com/hashicorp/go-hclog" 9 "github.com/hashicorp/go-memdb" 10 "github.com/hashicorp/nomad/nomad" 11 "github.com/hashicorp/raft" 12 ) 13 14 // FSMState returns a dump of the FSM state as found in data-dir, as of lastIndx value 15 func FSMState(p string, plastIdx int64) (interface{}, error) { 16 store, firstIdx, lastIdx, err := RaftStateInfo(filepath.Join(p, "raft.db")) 17 if err != nil { 18 return nil, fmt.Errorf("failed to open raft database %v: %v", p, err) 19 } 20 defer store.Close() 21 22 snaps, err := raft.NewFileSnapshotStore(p, 1000, os.Stderr) 23 if err != nil { 24 return nil, fmt.Errorf("failed to open snapshot dir: %v", err) 25 } 26 27 logger := hclog.L() 28 29 // use dummy non-enabled FSM dependencies 30 periodicDispatch := nomad.NewPeriodicDispatch(logger, nil) 31 blockedEvals := nomad.NewBlockedEvals(nil, logger) 32 evalBroker, err := nomad.NewEvalBroker(1, 1, 1, 1) 33 if err != nil { 34 return nil, err 35 } 36 fsmConfig := &nomad.FSMConfig{ 37 EvalBroker: evalBroker, 38 Periodic: periodicDispatch, 39 Blocked: blockedEvals, 40 Logger: logger, 41 Region: "default", 42 } 43 44 fsm, err := nomad.NewFSM(fsmConfig) 45 if err != nil { 46 return nil, err 47 } 48 49 // restore from snapshot first 50 sFirstIdx, err := restoreFromSnapshot(fsm, snaps, logger) 51 if err != nil { 52 return nil, err 53 } 54 55 if sFirstIdx+1 < firstIdx { 56 return nil, fmt.Errorf("missing logs after snapshot [%v,%v]", sFirstIdx+1, firstIdx-1) 57 } else if sFirstIdx > 0 { 58 firstIdx = sFirstIdx + 1 59 } 60 61 lastIdx = lastIndex(lastIdx, plastIdx) 62 63 for i := firstIdx; i <= lastIdx; i++ { 64 var e raft.Log 65 err := store.GetLog(i, &e) 66 if err != nil { 67 return nil, fmt.Errorf("failed to read log entry at index %d: %v, firstIdx: %d, lastIdx: %d", i, err, firstIdx, lastIdx) 68 } 69 70 if e.Type == raft.LogCommand { 71 fsm.Apply(&e) 72 } 73 } 74 75 state := fsm.State() 76 result := map[string][]interface{}{ 77 "ACLPolicies": toArray(state.ACLPolicies(nil)), 78 "ACLTokens": toArray(state.ACLTokens(nil)), 79 "Allocs": toArray(state.Allocs(nil)), 80 "CSIPlugins": toArray(state.CSIPlugins(nil)), 81 "CSIVolumes": toArray(state.CSIVolumes(nil)), 82 "Deployments": toArray(state.Deployments(nil)), 83 "Evals": toArray(state.Evals(nil)), 84 "Indexes": toArray(state.Indexes()), 85 "JobSummaries": toArray(state.JobSummaries(nil)), 86 "JobVersions": toArray(state.JobVersions(nil)), 87 "Jobs": toArray(state.Jobs(nil)), 88 "Nodes": toArray(state.Nodes(nil)), 89 "PeriodicLaunches": toArray(state.PeriodicLaunches(nil)), 90 "SITokenAccessors": toArray(state.SITokenAccessors(nil)), 91 "ScalingEvents": toArray(state.ScalingEvents(nil)), 92 "ScalingPolicies": toArray(state.ScalingPolicies(nil)), 93 "VaultAccessors": toArray(state.VaultAccessors(nil)), 94 } 95 96 insertEnterpriseState(result, state) 97 98 return result, nil 99 } 100 101 func restoreFromSnapshot(fsm raft.FSM, snaps raft.SnapshotStore, logger hclog.Logger) (uint64, error) { 102 logger = logger.Named("restoreFromSnapshot") 103 snapshots, err := snaps.List() 104 if err != nil { 105 return 0, err 106 } 107 logger.Debug("found snapshots", "count", len(snapshots)) 108 109 for _, snapshot := range snapshots { 110 _, source, err := snaps.Open(snapshot.ID) 111 if err != nil { 112 logger.Warn("failed to open a snapshot", "snapshot_id", snapshot.ID, "error", err) 113 continue 114 } 115 116 err = fsm.Restore(source) 117 source.Close() 118 if err != nil { 119 logger.Warn("failed to restore a snapshot", "snapshot_id", snapshot.ID, "error", err) 120 continue 121 } 122 123 return snapshot.Index, nil 124 } 125 126 return 0, nil 127 } 128 129 func lastIndex(raftLastIdx uint64, cliLastIdx int64) uint64 { 130 switch { 131 case cliLastIdx < 0: 132 if raftLastIdx > uint64(-cliLastIdx) { 133 return raftLastIdx - uint64(-cliLastIdx) 134 } else { 135 return 0 136 } 137 case cliLastIdx == 0: 138 return raftLastIdx 139 case uint64(cliLastIdx) < raftLastIdx: 140 return uint64(cliLastIdx) 141 default: 142 return raftLastIdx 143 } 144 } 145 146 func toArray(iter memdb.ResultIterator, err error) []interface{} { 147 if err != nil { 148 return []interface{}{err} 149 } 150 151 r := []interface{}{} 152 153 if iter != nil { 154 item := iter.Next() 155 for item != nil { 156 r = append(r, item) 157 item = iter.Next() 158 } 159 } 160 161 return r 162 }