snapshot.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package migrate
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "hash/crc32"
  7. "io/ioutil"
  8. "log"
  9. "os"
  10. "path"
  11. "sort"
  12. "strconv"
  13. "strings"
  14. raftpb "github.com/coreos/etcd/raft/raftpb"
  15. )
  16. type Snapshot4 struct {
  17. State []byte `json:"state"`
  18. LastIndex uint64 `json:"lastIndex"`
  19. LastTerm uint64 `json:"lastTerm"`
  20. Peers []struct {
  21. Name string `json:"name"`
  22. ConnectionString string `json:"connectionString"`
  23. } `json:"peers"`
  24. //TODO(bcwaldon): is this needed?
  25. //Path string `json:"path"`
  26. }
  27. func (s *Snapshot4) Snapshot5() *raftpb.Snapshot {
  28. snap5 := raftpb.Snapshot{
  29. Data: s.State,
  30. Index: int64(s.LastIndex),
  31. Term: int64(s.LastTerm),
  32. Nodes: make([]int64, len(s.Peers)),
  33. }
  34. for i, p := range s.Peers {
  35. snap5.Nodes[i] = hashName(p.Name)
  36. }
  37. return &snap5
  38. }
  39. func DecodeLatestSnapshot4FromDir(snapdir string) (*Snapshot4, error) {
  40. fname, err := FindLatestFile(snapdir)
  41. if err != nil {
  42. return nil, err
  43. }
  44. if fname == "" {
  45. return nil, nil
  46. }
  47. snappath := path.Join(snapdir, fname)
  48. log.Printf("Decoding snapshot from %s", snappath)
  49. return DecodeSnapshot4FromFile(snappath)
  50. }
  51. // FindLatestFile identifies the "latest" filename in a given directory
  52. // by sorting all the files and choosing the highest value.
  53. func FindLatestFile(dirpath string) (string, error) {
  54. dir, err := os.OpenFile(dirpath, os.O_RDONLY, 0)
  55. if err != nil {
  56. if os.IsNotExist(err) {
  57. err = nil
  58. }
  59. return "", err
  60. }
  61. defer dir.Close()
  62. fnames, err := dir.Readdirnames(-1)
  63. if err != nil {
  64. return "", err
  65. }
  66. if len(fnames) == 0 {
  67. return "", nil
  68. }
  69. names, err := NewSnapshotFileNames(fnames)
  70. if err != nil {
  71. return "", err
  72. }
  73. return names[len(names)-1].FileName, nil
  74. }
  75. func DecodeSnapshot4FromFile(path string) (*Snapshot4, error) {
  76. // Read snapshot data.
  77. f, err := os.OpenFile(path, os.O_RDONLY, 0)
  78. if err != nil {
  79. return nil, err
  80. }
  81. defer f.Close()
  82. return DecodeSnapshot4(f)
  83. }
  84. func DecodeSnapshot4(f *os.File) (*Snapshot4, error) {
  85. // Verify checksum
  86. var checksum uint32
  87. n, err := fmt.Fscanf(f, "%08x\n", &checksum)
  88. if err != nil {
  89. return nil, err
  90. } else if n != 1 {
  91. return nil, errors.New("miss heading checksum")
  92. }
  93. // Load remaining snapshot contents.
  94. b, err := ioutil.ReadAll(f)
  95. if err != nil {
  96. return nil, err
  97. }
  98. // Generate checksum.
  99. byteChecksum := crc32.ChecksumIEEE(b)
  100. if uint32(checksum) != byteChecksum {
  101. return nil, errors.New("bad checksum")
  102. }
  103. // Decode snapshot.
  104. snapshot := new(Snapshot4)
  105. if err = json.Unmarshal(b, snapshot); err != nil {
  106. return nil, err
  107. }
  108. return snapshot, nil
  109. }
  110. func NewSnapshotFileNames(names []string) ([]SnapshotFileName, error) {
  111. s := make([]SnapshotFileName, 0)
  112. for _, n := range names {
  113. trimmed := strings.TrimSuffix(n, ".ss")
  114. if trimmed == n {
  115. return nil, fmt.Errorf("file %q does not have .ss extension", n)
  116. }
  117. parts := strings.SplitN(trimmed, "_", 2)
  118. if len(parts) != 2 {
  119. return nil, fmt.Errorf("unrecognized file name format %q", n)
  120. }
  121. fn := SnapshotFileName{FileName: n}
  122. var err error
  123. fn.Term, err = strconv.ParseUint(parts[0], 10, 64)
  124. if err != nil {
  125. return nil, fmt.Errorf("unable to parse term from filename %q: %v", err)
  126. }
  127. fn.Index, err = strconv.ParseUint(parts[1], 10, 64)
  128. if err != nil {
  129. return nil, fmt.Errorf("unable to parse index from filename %q: %v", err)
  130. }
  131. s = append(s, fn)
  132. }
  133. sortable := SnapshotFileNames(s)
  134. sort.Sort(&sortable)
  135. return s, nil
  136. }
  137. type SnapshotFileNames []SnapshotFileName
  138. type SnapshotFileName struct {
  139. FileName string
  140. Term uint64
  141. Index uint64
  142. }
  143. func (n *SnapshotFileNames) Less(i, j int) bool {
  144. iTerm, iIndex := (*n)[i].Term, (*n)[i].Index
  145. jTerm, jIndex := (*n)[j].Term, (*n)[j].Index
  146. return iTerm < jTerm || (iTerm == jTerm && iIndex < jIndex)
  147. }
  148. func (n *SnapshotFileNames) Swap(i, j int) {
  149. (*n)[i], (*n)[j] = (*n)[j], (*n)[i]
  150. }
  151. func (n *SnapshotFileNames) Len() int {
  152. return len([]SnapshotFileName(*n))
  153. }