| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- package migrate
- import (
- "encoding/json"
- "errors"
- "fmt"
- "hash/crc32"
- "io/ioutil"
- "log"
- "net/url"
- "os"
- "path"
- "sort"
- "strconv"
- "strings"
- "time"
- raftpb "github.com/coreos/etcd/raft/raftpb"
- )
- type Snapshot4 struct {
- State []byte `json:"state"`
- LastIndex uint64 `json:"lastIndex"`
- LastTerm uint64 `json:"lastTerm"`
- Peers []struct {
- Name string `json:"name"`
- ConnectionString string `json:"connectionString"`
- } `json:"peers"`
- }
- type sstore struct {
- Root *node
- CurrentIndex uint64
- CurrentVersion int
- }
- type node struct {
- Path string
- CreatedIndex uint64
- ModifiedIndex uint64
- Parent *node `json:"-"` // should not encode this field! avoid circular dependency.
- ExpireTime time.Time
- ACL string
- Value string // for key-value pair
- Children map[string]*node // for directory
- }
- func replacePathNames(n *node, s1, s2 string) {
- n.Path = path.Clean(strings.Replace(n.Path, s1, s2, 1))
- for _, c := range n.Children {
- replacePathNames(c, s1, s2)
- }
- }
- func pullNodesFromEtcd(n *node) map[string]uint64 {
- out := make(map[string]uint64)
- machines := n.Children["machines"]
- for name, c := range machines.Children {
- q, err := url.ParseQuery(c.Value)
- if err != nil {
- log.Fatal("Couldn't parse old query string value")
- }
- etcdurl := q.Get("etcd")
- rafturl := q.Get("raft")
- m := generateNodeMember(name, rafturl, etcdurl)
- out[m.Name] = uint64(m.ID)
- }
- return out
- }
- func fixEtcd(n *node) {
- n.Path = "/0"
- machines := n.Children["machines"]
- n.Children["members"] = &node{
- Path: "/0/members",
- CreatedIndex: machines.CreatedIndex,
- ModifiedIndex: machines.ModifiedIndex,
- ExpireTime: machines.ExpireTime,
- ACL: machines.ACL,
- Children: make(map[string]*node),
- }
- for name, c := range machines.Children {
- q, err := url.ParseQuery(c.Value)
- if err != nil {
- log.Fatal("Couldn't parse old query string value")
- }
- etcdurl := q.Get("etcd")
- rafturl := q.Get("raft")
- m := generateNodeMember(name, rafturl, etcdurl)
- attrBytes, err := json.Marshal(m.attributes)
- if err != nil {
- log.Fatal("Couldn't marshal attributes")
- }
- raftBytes, err := json.Marshal(m.raftAttributes)
- if err != nil {
- log.Fatal("Couldn't marshal raft attributes")
- }
- newNode := &node{
- Path: path.Join("/0/members", m.ID.String()),
- CreatedIndex: c.CreatedIndex,
- ModifiedIndex: c.ModifiedIndex,
- ExpireTime: c.ExpireTime,
- ACL: c.ACL,
- Children: map[string]*node{
- "attributes": &node{
- Path: path.Join("/0/members", m.ID.String(), "attributes"),
- CreatedIndex: c.CreatedIndex,
- ModifiedIndex: c.ModifiedIndex,
- ExpireTime: c.ExpireTime,
- ACL: c.ACL,
- Value: string(attrBytes),
- },
- "raftAttributes": &node{
- Path: path.Join("/0/members", m.ID.String(), "raftAttributes"),
- CreatedIndex: c.CreatedIndex,
- ModifiedIndex: c.ModifiedIndex,
- ExpireTime: c.ExpireTime,
- ACL: c.ACL,
- Value: string(raftBytes),
- },
- },
- }
- n.Children["members"].Children[m.ID.String()] = newNode
- }
- delete(n.Children, "machines")
- }
- func mangleRoot(n *node) *node {
- newRoot := &node{
- Path: "/",
- CreatedIndex: n.CreatedIndex,
- ModifiedIndex: n.ModifiedIndex,
- ExpireTime: n.ExpireTime,
- ACL: n.ACL,
- Children: make(map[string]*node),
- }
- newRoot.Children["1"] = n
- etcd := n.Children["_etcd"]
- delete(n.Children, "_etcd")
- replacePathNames(n, "/", "/1/")
- fixEtcd(etcd)
- newRoot.Children["0"] = etcd
- return newRoot
- }
- func (s *Snapshot4) GetNodesFromStore() map[string]uint64 {
- st := &sstore{}
- if err := json.Unmarshal(s.State, st); err != nil {
- log.Fatal("Couldn't unmarshal snapshot")
- }
- etcd := st.Root.Children["_etcd"]
- return pullNodesFromEtcd(etcd)
- }
- func (s *Snapshot4) Snapshot2() *raftpb.Snapshot {
- st := &sstore{}
- if err := json.Unmarshal(s.State, st); err != nil {
- log.Fatal("Couldn't unmarshal snapshot")
- }
- st.Root = mangleRoot(st.Root)
- newState, err := json.Marshal(st)
- if err != nil {
- log.Fatal("Couldn't re-marshal new snapshot")
- }
- nodes := s.GetNodesFromStore()
- nodeList := make([]uint64, 0)
- for _, v := range nodes {
- nodeList = append(nodeList, v)
- }
- snap2 := raftpb.Snapshot{
- Data: newState,
- Metadata: raftpb.SnapshotMetadata{
- Index: s.LastIndex,
- Term: s.LastTerm,
- ConfState: raftpb.ConfState{
- Nodes: nodeList,
- },
- },
- }
- return &snap2
- }
- func DecodeLatestSnapshot4FromDir(snapdir string) (*Snapshot4, error) {
- fname, err := FindLatestFile(snapdir)
- if err != nil {
- return nil, err
- }
- if fname == "" {
- return nil, nil
- }
- snappath := path.Join(snapdir, fname)
- log.Printf("Decoding snapshot from %s", snappath)
- return DecodeSnapshot4FromFile(snappath)
- }
- // FindLatestFile identifies the "latest" filename in a given directory
- // by sorting all the files and choosing the highest value.
- func FindLatestFile(dirpath string) (string, error) {
- dir, err := os.OpenFile(dirpath, os.O_RDONLY, 0)
- if err != nil {
- if os.IsNotExist(err) {
- err = nil
- }
- return "", err
- }
- defer dir.Close()
- fnames, err := dir.Readdirnames(-1)
- if err != nil {
- return "", err
- }
- if len(fnames) == 0 {
- return "", nil
- }
- names, err := NewSnapshotFileNames(fnames)
- if err != nil {
- return "", err
- }
- return names[len(names)-1].FileName, nil
- }
- func DecodeSnapshot4FromFile(path string) (*Snapshot4, error) {
- // Read snapshot data.
- f, err := os.OpenFile(path, os.O_RDONLY, 0)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- return DecodeSnapshot4(f)
- }
- func DecodeSnapshot4(f *os.File) (*Snapshot4, error) {
- // Verify checksum
- var checksum uint32
- n, err := fmt.Fscanf(f, "%08x\n", &checksum)
- if err != nil {
- return nil, err
- } else if n != 1 {
- return nil, errors.New("miss heading checksum")
- }
- // Load remaining snapshot contents.
- b, err := ioutil.ReadAll(f)
- if err != nil {
- return nil, err
- }
- // Generate checksum.
- byteChecksum := crc32.ChecksumIEEE(b)
- if uint32(checksum) != byteChecksum {
- return nil, errors.New("bad checksum")
- }
- // Decode snapshot.
- snapshot := new(Snapshot4)
- if err = json.Unmarshal(b, snapshot); err != nil {
- return nil, err
- }
- return snapshot, nil
- }
- func NewSnapshotFileNames(names []string) ([]SnapshotFileName, error) {
- s := make([]SnapshotFileName, 0)
- for _, n := range names {
- trimmed := strings.TrimSuffix(n, ".ss")
- if trimmed == n {
- return nil, fmt.Errorf("file %q does not have .ss extension", n)
- }
- parts := strings.SplitN(trimmed, "_", 2)
- if len(parts) != 2 {
- return nil, fmt.Errorf("unrecognized file name format %q", n)
- }
- fn := SnapshotFileName{FileName: n}
- var err error
- fn.Term, err = strconv.ParseUint(parts[0], 10, 64)
- if err != nil {
- return nil, fmt.Errorf("unable to parse term from filename %q: %v", n, err)
- }
- fn.Index, err = strconv.ParseUint(parts[1], 10, 64)
- if err != nil {
- return nil, fmt.Errorf("unable to parse index from filename %q: %v", n, err)
- }
- s = append(s, fn)
- }
- sortable := SnapshotFileNames(s)
- sort.Sort(&sortable)
- return s, nil
- }
- type SnapshotFileNames []SnapshotFileName
- type SnapshotFileName struct {
- FileName string
- Term uint64
- Index uint64
- }
- func (n *SnapshotFileNames) Less(i, j int) bool {
- iTerm, iIndex := (*n)[i].Term, (*n)[i].Index
- jTerm, jIndex := (*n)[j].Term, (*n)[j].Index
- return iTerm < jTerm || (iTerm == jTerm && iIndex < jIndex)
- }
- func (n *SnapshotFileNames) Swap(i, j int) {
- (*n)[i], (*n)[j] = (*n)[j], (*n)[i]
- }
- func (n *SnapshotFileNames) Len() int {
- return len([]SnapshotFileName(*n))
- }
|