1package raft 2 3import ( 4 "bytes" 5 "encoding/json" 6 "io/ioutil" 7) 8 9// ReadPeersJSON consumes a legacy peers.json file in the format of the old JSON 10// peer store and creates a new-style configuration structure. This can be used 11// to migrate this data or perform manual recovery when running protocol versions 12// that can interoperate with older, unversioned Raft servers. This should not be 13// used once server IDs are in use, because the old peers.json file didn't have 14// support for these, nor non-voter suffrage types. 15func ReadPeersJSON(path string) (Configuration, error) { 16 // Read in the file. 17 buf, err := ioutil.ReadFile(path) 18 if err != nil { 19 return Configuration{}, err 20 } 21 22 // Parse it as JSON. 23 var peers []string 24 dec := json.NewDecoder(bytes.NewReader(buf)) 25 if err := dec.Decode(&peers); err != nil { 26 return Configuration{}, err 27 } 28 29 // Map it into the new-style configuration structure. We can only specify 30 // voter roles here, and the ID has to be the same as the address. 31 var configuration Configuration 32 for _, peer := range peers { 33 server := Server{ 34 Suffrage: Voter, 35 ID: ServerID(peer), 36 Address: ServerAddress(peer), 37 } 38 configuration.Servers = append(configuration.Servers, server) 39 } 40 41 // We should only ingest valid configurations. 42 if err := checkConfiguration(configuration); err != nil { 43 return Configuration{}, err 44 } 45 return configuration, nil 46} 47 48// configEntry is used when decoding a new-style peers.json. 49type configEntry struct { 50 // ID is the ID of the server (a UUID, usually). 51 ID ServerID `json:"id"` 52 53 // Address is the host:port of the server. 54 Address ServerAddress `json:"address"` 55 56 // NonVoter controls the suffrage. We choose this sense so people 57 // can leave this out and get a Voter by default. 58 NonVoter bool `json:"non_voter"` 59} 60 61// ReadConfigJSON reads a new-style peers.json and returns a configuration 62// structure. This can be used to perform manual recovery when running protocol 63// versions that use server IDs. 64func ReadConfigJSON(path string) (Configuration, error) { 65 // Read in the file. 66 buf, err := ioutil.ReadFile(path) 67 if err != nil { 68 return Configuration{}, err 69 } 70 71 // Parse it as JSON. 72 var peers []configEntry 73 dec := json.NewDecoder(bytes.NewReader(buf)) 74 if err := dec.Decode(&peers); err != nil { 75 return Configuration{}, err 76 } 77 78 // Map it into the new-style configuration structure. 79 var configuration Configuration 80 for _, peer := range peers { 81 suffrage := Voter 82 if peer.NonVoter { 83 suffrage = Nonvoter 84 } 85 server := Server{ 86 Suffrage: suffrage, 87 ID: peer.ID, 88 Address: peer.Address, 89 } 90 configuration.Servers = append(configuration.Servers, server) 91 } 92 93 // We should only ingest valid configurations. 94 if err := checkConfiguration(configuration); err != nil { 95 return Configuration{}, err 96 } 97 return configuration, nil 98} 99