v2_admin.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. package etcd
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. "net/url"
  7. "path/filepath"
  8. "strconv"
  9. "strings"
  10. "github.com/coreos/etcd/config"
  11. "github.com/coreos/etcd/store"
  12. )
  13. const (
  14. stateFollower = "follower"
  15. stateCandidate = "candidate"
  16. stateLeader = "leader"
  17. )
  18. // machineMessage represents information about a peer or standby in the registry.
  19. type machineMessage struct {
  20. Name string `json:"name"`
  21. State string `json:"state"`
  22. ClientURL string `json:"clientURL"`
  23. PeerURL string `json:"peerURL"`
  24. }
  25. type context struct {
  26. MinVersion int `json:"minVersion"`
  27. MaxVersion int `json:"maxVersion"`
  28. ClientURL string `json:"clientURL"`
  29. PeerURL string `json:"peerURL"`
  30. }
  31. func (p *participant) serveAdminConfig(w http.ResponseWriter, r *http.Request) error {
  32. switch r.Method {
  33. case "GET":
  34. case "PUT":
  35. if !p.node.IsLeader() {
  36. return p.redirect(w, r, p.node.Leader())
  37. }
  38. c := p.clusterConfig()
  39. if err := json.NewDecoder(r.Body).Decode(c); err != nil {
  40. return err
  41. }
  42. c.Sanitize()
  43. if err := p.setClusterConfig(c); err != nil {
  44. return err
  45. }
  46. default:
  47. return allow(w, "GET", "PUT")
  48. }
  49. w.Header().Set("Content-Type", "application/json")
  50. json.NewEncoder(w).Encode(p.clusterConfig())
  51. return nil
  52. }
  53. func (p *participant) serveAdminMachines(w http.ResponseWriter, r *http.Request) error {
  54. name := strings.TrimPrefix(r.URL.Path, v2adminMachinesPrefix)
  55. switch r.Method {
  56. case "GET":
  57. var info interface{}
  58. var err error
  59. if name != "" {
  60. info, err = p.someMachineMessage(name)
  61. } else {
  62. info, err = p.allMachineMessages()
  63. }
  64. if err != nil {
  65. return err
  66. }
  67. w.Header().Set("Content-Type", "application/json")
  68. json.NewEncoder(w).Encode(info)
  69. case "PUT":
  70. if !p.node.IsLeader() {
  71. return p.redirect(w, r, p.node.Leader())
  72. }
  73. id, err := strconv.ParseInt(name, 0, 64)
  74. if err != nil {
  75. return err
  76. }
  77. info := &context{}
  78. if err := json.NewDecoder(r.Body).Decode(info); err != nil {
  79. return err
  80. }
  81. return p.add(id, info.PeerURL, info.ClientURL)
  82. case "DELETE":
  83. if !p.node.IsLeader() {
  84. return p.redirect(w, r, p.node.Leader())
  85. }
  86. id, err := strconv.ParseInt(name, 0, 64)
  87. if err != nil {
  88. return err
  89. }
  90. return p.remove(id)
  91. default:
  92. return allow(w, "GET", "PUT", "DELETE")
  93. }
  94. return nil
  95. }
  96. func (p *participant) clusterConfig() *config.ClusterConfig {
  97. c := config.NewClusterConfig()
  98. // This is used for backward compatibility because it doesn't
  99. // set cluster config in older version.
  100. if e, err := p.Get(v2configKVPrefix, false, false); err == nil {
  101. json.Unmarshal([]byte(*e.Node.Value), c)
  102. }
  103. return c
  104. }
  105. func (p *participant) setClusterConfig(c *config.ClusterConfig) error {
  106. b, err := json.Marshal(c)
  107. if err != nil {
  108. return err
  109. }
  110. if _, err := p.Set(v2configKVPrefix, false, string(b), store.Permanent); err != nil {
  111. return err
  112. }
  113. return nil
  114. }
  115. // someMachineMessage return machine message of specified name.
  116. func (p *participant) someMachineMessage(name string) (*machineMessage, error) {
  117. pp := filepath.Join(v2machineKVPrefix, name)
  118. e, err := p.Get(pp, false, false)
  119. if err != nil {
  120. return nil, err
  121. }
  122. lead := fmt.Sprint(p.node.Leader())
  123. return newMachineMessage(e.Node, lead), nil
  124. }
  125. func (p *participant) allMachineMessages() ([]*machineMessage, error) {
  126. e, err := p.Get(v2machineKVPrefix, false, false)
  127. if err != nil {
  128. return nil, err
  129. }
  130. lead := fmt.Sprint(p.node.Leader())
  131. ms := make([]*machineMessage, len(e.Node.Nodes))
  132. for i, n := range e.Node.Nodes {
  133. ms[i] = newMachineMessage(n, lead)
  134. }
  135. return ms, nil
  136. }
  137. func newMachineMessage(n *store.NodeExtern, lead string) *machineMessage {
  138. _, name := filepath.Split(n.Key)
  139. q, err := url.ParseQuery(*n.Value)
  140. if err != nil {
  141. panic("fail to parse the info for machine " + name)
  142. }
  143. m := &machineMessage{
  144. Name: name,
  145. State: stateFollower,
  146. ClientURL: q["etcd"][0],
  147. PeerURL: q["raft"][0],
  148. }
  149. if name == lead {
  150. m.State = stateLeader
  151. }
  152. return m
  153. }