github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/helper/raftutil/state.go (about) 1 package raftutil 2 3 import ( 4 "bytes" 5 "fmt" 6 "os" 7 "path/filepath" 8 9 "github.com/hashicorp/go-msgpack/codec" 10 "github.com/hashicorp/nomad/nomad/structs" 11 "github.com/hashicorp/raft" 12 raftboltdb "github.com/hashicorp/raft-boltdb" 13 ) 14 15 // RaftStateInfo returns info about the nomad state, as found in the passed data-dir directory 16 func RaftStateInfo(p string) (store *raftboltdb.BoltStore, firstIdx uint64, lastIdx uint64, err error) { 17 s, err := raftboltdb.NewBoltStore(p) 18 if err != nil { 19 return nil, 0, 0, fmt.Errorf("failed to open raft logs: %v", err) 20 } 21 22 firstIdx, err = s.FirstIndex() 23 if err != nil { 24 return nil, 0, 0, fmt.Errorf("failed to fetch first index: %v", err) 25 } 26 27 lastIdx, err = s.LastIndex() 28 if err != nil { 29 return nil, 0, 0, fmt.Errorf("failed to fetch last index: %v", err) 30 } 31 32 return s, firstIdx, lastIdx, nil 33 } 34 35 // LogEntries returns the log entries as found in raft log in the passed data-dir directory 36 func LogEntries(p string) (logs []interface{}, warnings []error, err error) { 37 store, firstIdx, lastIdx, err := RaftStateInfo(p) 38 if err != nil { 39 return nil, nil, fmt.Errorf("failed to open raft logs: %v", err) 40 } 41 defer store.Close() 42 43 result := make([]interface{}, 0, lastIdx-firstIdx+1) 44 for i := firstIdx; i <= lastIdx; i++ { 45 var e raft.Log 46 err := store.GetLog(i, &e) 47 if err != nil { 48 warnings = append(warnings, fmt.Errorf("failed to read log entry at index %d (firstIdx: %d, lastIdx: %d): %v", i, firstIdx, lastIdx, err)) 49 continue 50 } 51 52 m, err := decode(&e) 53 if err != nil { 54 warnings = append(warnings, fmt.Errorf("failed to decode log entry at index %d: %v", i, err)) 55 continue 56 } 57 58 result = append(result, m) 59 } 60 61 return result, warnings, nil 62 } 63 64 type logMessage struct { 65 LogType string 66 Term uint64 67 Index uint64 68 69 CommandType string `json:",omitempty"` 70 IgnoreUnknownTypeFlag bool `json:",omitempty"` 71 Body interface{} `json:",omitempty"` 72 } 73 74 func decode(e *raft.Log) (*logMessage, error) { 75 m := &logMessage{ 76 LogType: logTypes[e.Type], 77 Term: e.Term, 78 Index: e.Index, 79 } 80 81 if m.LogType == "" { 82 m.LogType = fmt.Sprintf("%d", e.Type) 83 } 84 85 var data []byte 86 if e.Type == raft.LogCommand { 87 if len(e.Data) == 0 { 88 return nil, fmt.Errorf("command did not include data") 89 } 90 91 msgType := structs.MessageType(e.Data[0]) 92 93 m.CommandType = commandName(msgType & ^structs.IgnoreUnknownTypeFlag) 94 m.IgnoreUnknownTypeFlag = (msgType & structs.IgnoreUnknownTypeFlag) != 0 95 96 data = e.Data[1:] 97 } else { 98 data = e.Data 99 } 100 101 if len(data) != 0 { 102 decoder := codec.NewDecoder(bytes.NewReader(data), structs.MsgpackHandle) 103 104 var v interface{} 105 var err error 106 if m.CommandType == commandName(structs.JobBatchDeregisterRequestType) { 107 var vr structs.JobBatchDeregisterRequest 108 err = decoder.Decode(&vr) 109 v = jsonifyJobBatchDeregisterRequest(&vr) 110 } else { 111 var vr interface{} 112 err = decoder.Decode(&vr) 113 v = vr 114 } 115 116 if err != nil { 117 fmt.Fprintf(os.Stderr, "failed to decode log entry at index %d: failed to decode body of %v.%v %v\n", e.Index, e.Type, m.CommandType, err) 118 v = "FAILED TO DECODE DATA" 119 } 120 fixTime(v) 121 m.Body = v 122 } 123 124 return m, nil 125 } 126 127 // jsonifyJobBatchDeregisterRequest special case JsonBatchDeregisterRequest object 128 // as the actual type is not json friendly. 129 func jsonifyJobBatchDeregisterRequest(v *structs.JobBatchDeregisterRequest) interface{} { 130 var data struct { 131 Jobs map[string]*structs.JobDeregisterOptions 132 Evals []*structs.Evaluation 133 structs.WriteRequest 134 } 135 data.Evals = v.Evals 136 data.WriteRequest = v.WriteRequest 137 138 data.Jobs = make(map[string]*structs.JobDeregisterOptions, len(v.Jobs)) 139 if len(v.Jobs) != 0 { 140 for k, v := range v.Jobs { 141 data.Jobs[k.Namespace+"."+k.ID] = v 142 } 143 } 144 return data 145 } 146 147 var logTypes = map[raft.LogType]string{ 148 raft.LogCommand: "LogCommand", 149 raft.LogNoop: "LogNoop", 150 raft.LogAddPeerDeprecated: "LogAddPeerDeprecated", 151 raft.LogRemovePeerDeprecated: "LogRemovePeerDeprecated", 152 raft.LogBarrier: "LogBarrier", 153 raft.LogConfiguration: "LogConfiguration", 154 } 155 156 func commandName(mt structs.MessageType) string { 157 n := msgTypeNames[mt] 158 if n != "" { 159 return n 160 } 161 162 return fmt.Sprintf("%v", mt) 163 } 164 165 // FindRaftFile finds raft.db and returns path 166 func FindRaftFile(p string) (raftpath string, err error) { 167 // Try known locations before traversal to avoid walking deep structure 168 if _, err = os.Stat(filepath.Join(p, "server", "raft", "raft.db")); err == nil { 169 raftpath = filepath.Join(p, "server", "raft", "raft.db") 170 } else if _, err = os.Stat(filepath.Join(p, "raft", "raft.db")); err == nil { 171 raftpath = filepath.Join(p, "raft", "raft.db") 172 } else if _, err = os.Stat(filepath.Join(p, "raft.db")); err == nil { 173 raftpath = filepath.Join(p, "raft.db") 174 } else if _, err = os.Stat(p); err == nil && filepath.Ext(p) == ".db" { 175 // Also accept path to .db file 176 raftpath = p 177 } else { 178 raftpath, err = FindFileInPath("raft.db", p) 179 } 180 181 if err != nil { 182 return "", err 183 } 184 185 return raftpath, nil 186 } 187 188 // FindRaftDir finds raft.db and returns parent directory path 189 func FindRaftDir(p string) (raftpath string, err error) { 190 raftpath, err = FindRaftFile(p) 191 if err != nil { 192 return "", err 193 } 194 195 if raftpath == "" { 196 return "", fmt.Errorf("failed to find raft dir in %s", p) 197 } 198 199 return filepath.Dir(raftpath), nil 200 } 201 202 // FindFileInPath searches for file in path p 203 func FindFileInPath(file string, p string) (path string, err error) { 204 // Define walk function to find file 205 walkFn := func(walkPath string, info os.FileInfo, err error) error { 206 if err != nil { 207 return err 208 } 209 210 if filepath.Base(walkPath) == file { 211 path = walkPath 212 } 213 return nil 214 } 215 216 // Walk path p looking for file 217 walkErr := filepath.Walk(p, walkFn) 218 if walkErr != nil { 219 return "", walkErr 220 } 221 222 if path == "" { 223 return "", fmt.Errorf("File %s not found in path %s", file, p) 224 } 225 226 return path, nil 227 }